feat: NamedSource
This commit is contained in:
@@ -1,16 +1,25 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use nix_js::context::Context;
|
use nix_js::context::Context;
|
||||||
|
use nix_js::error::{Result, Source};
|
||||||
use nix_js::value::Value;
|
use nix_js::value::Value;
|
||||||
|
|
||||||
pub fn eval(expr: &str) -> Value {
|
pub fn eval(expr: &str) -> Value {
|
||||||
Context::new().unwrap().eval_code(expr).unwrap()
|
Context::new()
|
||||||
|
.unwrap()
|
||||||
|
.eval_code(Source::new_eval(expr.into()).unwrap())
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_result(expr: &str) -> Result<Value, nix_js::error::Error> {
|
pub fn eval_result(expr: &str) -> Result<Value> {
|
||||||
Context::new().unwrap().eval_code(expr)
|
Context::new()
|
||||||
|
.unwrap()
|
||||||
|
.eval_code(Source::new_eval(expr.into()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(expr: &str) -> String {
|
pub fn compile(expr: &str) -> String {
|
||||||
Context::new().unwrap().compile_code(expr).unwrap()
|
Context::new()
|
||||||
|
.unwrap()
|
||||||
|
.compile_code(Source::new_eval(expr.into()).unwrap())
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ function enrichError(error: unknown): Error {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use compact format for easy parsing (no regex needed)
|
|
||||||
// Format: NIX_STACK_FRAME:context:start:end:message
|
|
||||||
const nixStackLines = callStack.map((frame) => {
|
const nixStackLines = callStack.map((frame) => {
|
||||||
return `NIX_STACK_FRAME:context:${frame.span}:${frame.message}`;
|
return `NIX_STACK_FRAME:context:${frame.span}:${frame.message}`;
|
||||||
});
|
});
|
||||||
@@ -356,11 +354,14 @@ function call_impl(func: NixValue, arg: NixValue): NixValue {
|
|||||||
throw new Error(`attempt to call something which is not a function but ${typeName(forcedFunc)}`);
|
throw new Error(`attempt to call something which is not a function but ${typeName(forcedFunc)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string): NixValue => {
|
export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string, span: string): NixValue => {
|
||||||
if (forceBool(assertion)) {
|
if (forceBool(assertion)) {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
throw new CatchableError(`assertion '${assertionRaw}' failed`);
|
withContext("while evaluating assertion", span, () => {
|
||||||
|
throw new CatchableError(`assertion '${assertionRaw}' failed`);
|
||||||
|
});
|
||||||
|
throw "unreachable";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ifFunc = (cond: NixValue, consq: NixValue, alter: NixValue) => {
|
export const ifFunc = (cond: NixValue, consq: NixValue, alter: NixValue) => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use nix_js::context::Context;
|
use nix_js::{context::Context, error::Source};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
@@ -12,7 +12,8 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
args.next();
|
args.next();
|
||||||
let expr = args.next().unwrap();
|
let expr = args.next().unwrap();
|
||||||
match Context::new()?.eval_code(&expr) {
|
let src = Source::new_eval(expr)?;
|
||||||
|
match Context::new()?.eval_code(src) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
println!("{value}");
|
println!("{value}");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use nix_js::context::Context;
|
use nix_js::context::Context;
|
||||||
|
use nix_js::error::Source;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustyline::DefaultEditor;
|
use rustyline::DefaultEditor;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
@@ -31,7 +32,8 @@ fn main() -> Result<()> {
|
|||||||
eprintln!("Error: {}", err);
|
eprintln!("Error: {}", err);
|
||||||
} */
|
} */
|
||||||
} else {
|
} else {
|
||||||
match context.eval_code(&line) {
|
let src = Source::new_repl(line)?;
|
||||||
|
match context.eval_code(src) {
|
||||||
Ok(value) => println!("{value}"),
|
Ok(value) => println!("{value}"),
|
||||||
Err(err) => eprintln!("{:?}", miette::Report::new(err)),
|
Err(err) => eprintln!("{:?}", miette::Report::new(err)),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,11 @@ impl EscapeQuote for str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn encode_span(span: rnix::TextRange) -> String {
|
fn encode_span(span: rnix::TextRange) -> String {
|
||||||
format!("\"{}:{}\"", usize::from(span.start()), usize::from(span.end()))
|
format!(
|
||||||
|
"\"{}:{}\"",
|
||||||
|
usize::from(span.start()),
|
||||||
|
usize::from(span.end())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||||
@@ -78,7 +82,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
|||||||
format!("Nix.resolvePath(currentDir,{})", path_expr)
|
format!("Nix.resolvePath(currentDir,{})", path_expr)
|
||||||
}
|
}
|
||||||
&Ir::If(If {
|
&Ir::If(If {
|
||||||
cond, consq, alter, span
|
cond,
|
||||||
|
consq,
|
||||||
|
alter,
|
||||||
|
span: _,
|
||||||
}) => {
|
}) => {
|
||||||
let cond_code = ctx.get_ir(cond).compile(ctx);
|
let cond_code = ctx.get_ir(cond).compile(ctx);
|
||||||
let consq = ctx.get_ir(consq).compile(ctx);
|
let consq = ctx.get_ir(consq).compile(ctx);
|
||||||
@@ -129,14 +136,21 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
|||||||
// Only add context tracking if STACK_TRACE is enabled
|
// Only add context tracking if STACK_TRACE is enabled
|
||||||
if std::env::var("NIX_JS_STACK_TRACE").is_ok() {
|
if std::env::var("NIX_JS_STACK_TRACE").is_ok() {
|
||||||
let assertion_span = encode_span(ctx.get_ir(assertion).span());
|
let assertion_span = encode_span(ctx.get_ir(assertion).span());
|
||||||
|
let span = encode_span(span);
|
||||||
format!(
|
format!(
|
||||||
"Nix.assert(Nix.withContext(\"while evaluating the condition of the assert statement\",{},()=>({})),{},{})",
|
"Nix.assert(Nix.withContext(\"while evaluating the condition of the assert statement\",{},()=>({})),{},{},{})",
|
||||||
assertion_span, assertion_code, expr, assertion_raw.escape_quote()
|
assertion_span,
|
||||||
|
assertion_code,
|
||||||
|
expr,
|
||||||
|
assertion_raw.escape_quote(),
|
||||||
|
span
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"Nix.assert({},{},{})",
|
"Nix.assert({},{},{})",
|
||||||
assertion_code, expr, assertion_raw.escape_quote()
|
assertion_code,
|
||||||
|
expr,
|
||||||
|
assertion_raw.escape_quote()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,8 +424,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
|
|||||||
let parts: Vec<String> = self
|
let parts: Vec<String> = self
|
||||||
.parts
|
.parts
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.map(|part| {
|
||||||
.map(|(_idx, part)| {
|
|
||||||
let part_code = ctx.get_ir(*part).compile(ctx);
|
let part_code = ctx.get_ir(*part).compile(ctx);
|
||||||
if stack_trace_enabled {
|
if stack_trace_enabled {
|
||||||
let part_span = encode_span(ctx.get_ir(*part).span());
|
let part_span = encode_span(ctx.get_ir(*part).span());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
@@ -8,13 +8,12 @@ use rnix::TextRange;
|
|||||||
use string_interner::DefaultStringInterner;
|
use string_interner::DefaultStringInterner;
|
||||||
|
|
||||||
use crate::codegen::{CodegenContext, compile};
|
use crate::codegen::{CodegenContext, compile};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result, Source};
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
Arg, ArgId, Bool, Builtin, Downgrade as _, DowngradeContext, ExprId, ExprRef, Ir, Null, SymId,
|
Arg, ArgId, Bool, Builtin, Downgrade as _, DowngradeContext, ExprId, ExprRef, Ir, Null, SymId,
|
||||||
ToIr as _, synthetic_span,
|
ToIr as _, synthetic_span,
|
||||||
};
|
};
|
||||||
use crate::runtime::{Runtime, RuntimeContext};
|
use crate::runtime::{Runtime, RuntimeContext};
|
||||||
use crate::sourcemap::NixSourceMapBuilder;
|
|
||||||
use crate::store::{StoreBackend, StoreConfig};
|
use crate::store::{StoreBackend, StoreConfig};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -42,13 +41,13 @@ mod private {
|
|||||||
fn get_current_dir(&self) -> &Path {
|
fn get_current_dir(&self) -> &Path {
|
||||||
self.as_ref().get_current_dir()
|
self.as_ref().get_current_dir()
|
||||||
}
|
}
|
||||||
fn set_current_file(&mut self, path: PathBuf) {
|
fn set_current_file(&mut self, source: Source) {
|
||||||
self.as_mut().current_file = Some(path);
|
self.as_mut().current_file = Some(source);
|
||||||
}
|
}
|
||||||
fn compile_code(&mut self, expr: &str) -> Result<String> {
|
fn compile_code(&mut self, source: Source) -> Result<String> {
|
||||||
self.as_mut().compile_code(expr)
|
self.as_mut().compile_code(source)
|
||||||
}
|
}
|
||||||
fn get_current_source(&self) -> Option<Arc<str>> {
|
fn get_current_source(&self) -> Option<Source> {
|
||||||
self.as_ref().get_current_source()
|
self.as_ref().get_current_source()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,18 +81,12 @@ impl Context {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_code(&mut self, expr: &str) -> Result<Value> {
|
pub fn eval_code(&mut self, source: Source) -> Result<Value> {
|
||||||
tracing::info!("Starting evaluation");
|
tracing::info!("Starting evaluation");
|
||||||
self.ctx.current_file = Some(
|
self.ctx.current_file = Some(source.clone());
|
||||||
std::env::current_dir()
|
|
||||||
.map_err(|err| {
|
|
||||||
Error::internal(format!("Failed to get current working dir: {err}"))
|
|
||||||
})?
|
|
||||||
.join("__eval__.nix"),
|
|
||||||
);
|
|
||||||
|
|
||||||
tracing::debug!("Compiling code");
|
tracing::debug!("Compiling code");
|
||||||
let code = self.compile_code(expr)?;
|
let code = self.compile_code(source)?;
|
||||||
|
|
||||||
self.runtime.op_state().borrow_mut().put(self.store.clone());
|
self.runtime.op_state().borrow_mut().put(self.store.clone());
|
||||||
|
|
||||||
@@ -102,8 +95,8 @@ impl Context {
|
|||||||
.eval(format!("Nix.force({code})"), CtxPtr::new(&mut self.ctx))
|
.eval(format!("Nix.force({code})"), CtxPtr::new(&mut self.ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_code(&mut self, expr: &str) -> Result<String> {
|
pub fn compile_code(&mut self, source: Source) -> Result<String> {
|
||||||
self.ctx.compile_code(expr)
|
self.ctx.compile_code(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -120,10 +113,8 @@ pub(crate) struct Ctx {
|
|||||||
irs: Vec<Ir>,
|
irs: Vec<Ir>,
|
||||||
symbols: DefaultStringInterner,
|
symbols: DefaultStringInterner,
|
||||||
global: NonNull<HashMap<SymId, ExprId>>,
|
global: NonNull<HashMap<SymId, ExprId>>,
|
||||||
current_file: Option<PathBuf>,
|
current_file: Option<Source>,
|
||||||
source_map: HashMap<PathBuf, Arc<str>>,
|
current_source: Option<Source>,
|
||||||
current_source: Option<Arc<str>>,
|
|
||||||
js_source_maps: HashMap<PathBuf, sourcemap::SourceMap>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Ctx {
|
impl Default for Ctx {
|
||||||
@@ -216,9 +207,7 @@ impl Default for Ctx {
|
|||||||
irs,
|
irs,
|
||||||
global: unsafe { NonNull::new_unchecked(Box::leak(Box::new(global))) },
|
global: unsafe { NonNull::new_unchecked(Box::leak(Box::new(global))) },
|
||||||
current_file: None,
|
current_file: None,
|
||||||
source_map: HashMap::new(),
|
|
||||||
current_source: None,
|
current_source: None,
|
||||||
js_source_maps: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,27 +226,19 @@ impl Ctx {
|
|||||||
self.current_file
|
self.current_file
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("current_file is not set")
|
.expect("current_file is not set")
|
||||||
.parent()
|
.get_dir()
|
||||||
.expect("current_file doesn't have a parent dir")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_current_source(&self) -> Option<Arc<str>> {
|
pub(crate) fn get_current_source(&self) -> Option<Source> {
|
||||||
self.current_source.clone()
|
self.current_source.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_code(&mut self, expr: &str) -> Result<String> {
|
fn compile_code(&mut self, source: Source) -> Result<String> {
|
||||||
tracing::debug!("Parsing Nix expression");
|
tracing::debug!("Parsing Nix expression");
|
||||||
|
|
||||||
// Store source text for error reporting
|
|
||||||
let source: Arc<str> = Arc::from(expr);
|
|
||||||
self.current_source = Some(source.clone());
|
self.current_source = Some(source.clone());
|
||||||
|
|
||||||
// Store source in source_map if we have a current_file
|
let root = rnix::Root::parse(&source.src);
|
||||||
if let Some(ref file) = self.current_file {
|
|
||||||
self.source_map.insert(file.clone(), source.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let root = rnix::Root::parse(expr);
|
|
||||||
if !root.errors().is_empty() {
|
if !root.errors().is_empty() {
|
||||||
let error_msg = root.errors().iter().join("; ");
|
let error_msg = root.errors().iter().join("; ");
|
||||||
let err = Error::parse_error(error_msg).with_source(source);
|
let err = Error::parse_error(error_msg).with_source(source);
|
||||||
@@ -450,7 +431,7 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
result.ok_or_else(|| {
|
result.ok_or_else(|| {
|
||||||
Error::downgrade_error(format!("'{}' not found", self.get_sym(sym)))
|
Error::downgrade_error(format!("'{}' not found", self.get_sym(sym)))
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
.with_source(self.get_current_source().unwrap_or_else(|| Arc::from("")))
|
.with_source(self.get_current_source().expect("no source set"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,13 +456,13 @@ impl DowngradeContext for DowngradeCtx<'_> {
|
|||||||
fn get_span(&self, id: ExprId) -> rnix::TextRange {
|
fn get_span(&self, id: ExprId) -> rnix::TextRange {
|
||||||
dbg!(id);
|
dbg!(id);
|
||||||
if id.0 >= self.ctx.irs.len() {
|
if id.0 >= self.ctx.irs.len() {
|
||||||
return self.ctx.irs.get(id.0).unwrap().span()
|
return self.ctx.irs.get(id.0).unwrap().span();
|
||||||
}
|
}
|
||||||
let local_id = id.0 - self.ctx.irs.len();
|
let local_id = id.0 - self.ctx.irs.len();
|
||||||
self.irs.get(local_id).unwrap().as_ref().unwrap().span()
|
self.irs.get(local_id).unwrap().as_ref().unwrap().span()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_source(&self) -> Option<Arc<str>> {
|
fn get_current_source(&self) -> Option<Source> {
|
||||||
self.ctx.current_source.clone()
|
self.ctx.current_source.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,83 @@
|
|||||||
use miette::{Diagnostic, LabeledSpan, SourceSpan};
|
use miette::{Diagnostic, NamedSource, SourceSpan};
|
||||||
use std::sync::Arc;
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum SourceType {
|
||||||
|
/// dir
|
||||||
|
Eval(Arc<PathBuf>),
|
||||||
|
/// dir
|
||||||
|
Repl(Arc<PathBuf>),
|
||||||
|
/// file
|
||||||
|
File(Arc<PathBuf>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Source {
|
||||||
|
pub ty: SourceType,
|
||||||
|
pub src: Arc<str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Source {
|
||||||
|
type Error = Error;
|
||||||
|
fn try_from(value: &str) -> Result<Self> {
|
||||||
|
Source::new_eval(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Source> for NamedSource<Arc<str>> {
|
||||||
|
fn from(value: Source) -> Self {
|
||||||
|
let name = match value.ty {
|
||||||
|
SourceType::Eval(_) => "«eval»".into(),
|
||||||
|
SourceType::Repl(_) => "«repl»".into(),
|
||||||
|
SourceType::File(path) => path.as_os_str().to_string_lossy().to_string(),
|
||||||
|
};
|
||||||
|
NamedSource::new(name, value.src.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Source {
|
||||||
|
pub fn new_eval(src: String) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
ty: std::env::current_dir()
|
||||||
|
.map_err(|err| Error::internal(format!("Failed to get current working dir: {err}")))
|
||||||
|
.map(Arc::new)
|
||||||
|
.map(SourceType::Eval)?,
|
||||||
|
src: src.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_repl(src: String) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
ty: std::env::current_dir()
|
||||||
|
.map_err(|err| Error::internal(format!("Failed to get current working dir: {err}")))
|
||||||
|
.map(Arc::new)
|
||||||
|
.map(SourceType::Repl)?,
|
||||||
|
src: src.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_dir(&self) -> &Path {
|
||||||
|
use SourceType::*;
|
||||||
|
match &self.ty {
|
||||||
|
Eval(dir) | Repl(dir) => dir.as_ref(),
|
||||||
|
File(file) => file.as_path().parent().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, Diagnostic)]
|
#[derive(Error, Debug, Diagnostic)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Parse error: {message}")]
|
#[error("Parse error: {message}")]
|
||||||
#[diagnostic(code(nix::parse))]
|
#[diagnostic(code(nix::parse))]
|
||||||
ParseError {
|
ParseError {
|
||||||
#[source_code]
|
#[source_code]
|
||||||
src: Option<Arc<str>>,
|
src: Option<NamedSource<Arc<str>>>,
|
||||||
#[label("error occurred here")]
|
#[label("error occurred here")]
|
||||||
span: Option<SourceSpan>,
|
span: Option<SourceSpan>,
|
||||||
message: String,
|
message: String,
|
||||||
@@ -20,19 +87,17 @@ pub enum Error {
|
|||||||
#[diagnostic(code(nix::downgrade))]
|
#[diagnostic(code(nix::downgrade))]
|
||||||
DowngradeError {
|
DowngradeError {
|
||||||
#[source_code]
|
#[source_code]
|
||||||
src: Option<Arc<str>>,
|
src: Option<NamedSource<Arc<str>>>,
|
||||||
#[label("{message}")]
|
#[label("{message}")]
|
||||||
span: Option<SourceSpan>,
|
span: Option<SourceSpan>,
|
||||||
message: String,
|
message: String,
|
||||||
// #[related]
|
|
||||||
// related: Vec<LabeledSpan>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error("Evaluation error: {message}")]
|
#[error("Evaluation error: {message}")]
|
||||||
#[diagnostic(code(nix::eval))]
|
#[diagnostic(code(nix::eval))]
|
||||||
EvalError {
|
EvalError {
|
||||||
#[source_code]
|
#[source_code]
|
||||||
src: Option<Arc<str>>,
|
src: Option<NamedSource<Arc<str>>>,
|
||||||
#[label("error occurred here")]
|
#[label("error occurred here")]
|
||||||
span: Option<SourceSpan>,
|
span: Option<SourceSpan>,
|
||||||
message: String,
|
message: String,
|
||||||
@@ -67,19 +132,9 @@ impl Error {
|
|||||||
src: None,
|
src: None,
|
||||||
span: None,
|
span: None,
|
||||||
message: msg,
|
message: msg,
|
||||||
// related: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn downgrade_error_with_related(msg: String, related: Vec<LabeledSpan>) -> Self {
|
|
||||||
// Error::DowngradeError {
|
|
||||||
// src: None,
|
|
||||||
// span: None,
|
|
||||||
// message: msg,
|
|
||||||
// related,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn eval_error(msg: String, backtrace: Option<String>) -> Self {
|
pub fn eval_error(msg: String, backtrace: Option<String>) -> Self {
|
||||||
Error::EvalError {
|
Error::EvalError {
|
||||||
src: None,
|
src: None,
|
||||||
@@ -109,16 +164,10 @@ impl Error {
|
|||||||
span: source_span,
|
span: source_span,
|
||||||
message,
|
message,
|
||||||
},
|
},
|
||||||
Error::DowngradeError {
|
Error::DowngradeError { src, message, .. } => Error::DowngradeError {
|
||||||
src,
|
|
||||||
message,
|
|
||||||
// related,
|
|
||||||
..
|
|
||||||
} => Error::DowngradeError {
|
|
||||||
src,
|
src,
|
||||||
span: source_span,
|
span: source_span,
|
||||||
message,
|
message,
|
||||||
// related,
|
|
||||||
},
|
},
|
||||||
Error::EvalError {
|
Error::EvalError {
|
||||||
src,
|
src,
|
||||||
@@ -135,21 +184,13 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_source(self, source: Arc<str>) -> Self {
|
pub fn with_source(self, source: Source) -> Self {
|
||||||
let src = Some(source);
|
let src = Some(source.into());
|
||||||
match self {
|
match self {
|
||||||
Error::ParseError { span, message, .. } => Error::ParseError { src, span, message },
|
Error::ParseError { span, message, .. } => Error::ParseError { src, span, message },
|
||||||
Error::DowngradeError {
|
Error::DowngradeError { span, message, .. } => {
|
||||||
span,
|
Error::DowngradeError { src, span, message }
|
||||||
message,
|
}
|
||||||
// related,
|
|
||||||
..
|
|
||||||
} => Error::DowngradeError {
|
|
||||||
src,
|
|
||||||
span,
|
|
||||||
message,
|
|
||||||
// related,
|
|
||||||
},
|
|
||||||
Error::EvalError {
|
Error::EvalError {
|
||||||
span,
|
span,
|
||||||
message,
|
message,
|
||||||
@@ -207,10 +248,7 @@ pub(crate) fn parse_nix_stack(stack: &str) -> Vec<NixStackFrame> {
|
|||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let span = rnix::TextRange::new(
|
let span = rnix::TextRange::new(rnix::TextSize::from(start), rnix::TextSize::from(end));
|
||||||
rnix::TextSize::from(start),
|
|
||||||
rnix::TextSize::from(end)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Convert all frame types to context frames with descriptive messages
|
// Convert all frame types to context frames with descriptive messages
|
||||||
let message = match frame_type {
|
let message = match frame_type {
|
||||||
@@ -248,8 +286,12 @@ pub(crate) fn format_stack_trace(frames: &[NixStackFrame]) -> Vec<String> {
|
|||||||
|
|
||||||
// Reverse order: oldest first, newest last
|
// Reverse order: oldest first, newest last
|
||||||
for frame in frames.iter().rev() {
|
for frame in frames.iter().rev() {
|
||||||
lines.push(format!("{} at {}:{}",
|
lines.push(format!(
|
||||||
frame.message, usize::from(frame.span.start()), usize::from(frame.span.end())));
|
"{} at {}:{}",
|
||||||
|
frame.message,
|
||||||
|
usize::from(frame.span.start()),
|
||||||
|
usize::from(frame.span.end())
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
lines
|
lines
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use rusqlite::{Connection, OptionalExtension, params};
|
use rusqlite::{Connection, OptionalExtension, params};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use derive_more::{IsVariant, TryUnwrap, Unwrap};
|
use derive_more::{IsVariant, TryUnwrap, Unwrap};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rnix::{TextRange, ast};
|
use rnix::{TextRange, ast};
|
||||||
use std::sync::Arc;
|
|
||||||
use string_interner::symbol::SymbolU32;
|
use string_interner::symbol::SymbolU32;
|
||||||
|
|
||||||
use crate::context::SccInfo;
|
use crate::context::SccInfo;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result, Source};
|
||||||
use crate::value::format_symbol;
|
use crate::value::format_symbol;
|
||||||
use nix_js_macros::ir;
|
use nix_js_macros::ir;
|
||||||
|
|
||||||
@@ -32,7 +31,7 @@ pub trait DowngradeContext {
|
|||||||
fn replace_expr(&mut self, id: ExprId, expr: Ir);
|
fn replace_expr(&mut self, id: ExprId, expr: Ir);
|
||||||
fn reserve_slots(&mut self, slots: usize) -> impl Iterator<Item = ExprId> + Clone + use<Self>;
|
fn reserve_slots(&mut self, slots: usize) -> impl Iterator<Item = ExprId> + Clone + use<Self>;
|
||||||
fn get_span(&self, id: ExprId) -> TextRange;
|
fn get_span(&self, id: ExprId) -> TextRange;
|
||||||
fn get_current_source(&self) -> Option<Arc<str>>;
|
fn get_current_source(&self) -> Option<Source>;
|
||||||
|
|
||||||
fn with_param_scope<F, R>(&mut self, param: SymId, arg: ExprId, f: F) -> R
|
fn with_param_scope<F, R>(&mut self, param: SymId, arg: ExprId, f: F) -> R
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Assume no parse error
|
// Assume no parse error
|
||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use rnix::ast::{self, AstToken, Expr, HasEntry};
|
use rnix::ast::{self, AstToken, Expr, HasEntry};
|
||||||
use rowan::ast::AstNode;
|
use rowan::ast::AstNode;
|
||||||
|
|
||||||
@@ -23,7 +21,7 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for Expr {
|
|||||||
let span = error.syntax().text_range();
|
let span = error.syntax().text_range();
|
||||||
Err(self::Error::downgrade_error(error.to_string())
|
Err(self::Error::downgrade_error(error.to_string())
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))))
|
.with_source(ctx.get_current_source().expect("no source set")))
|
||||||
}
|
}
|
||||||
IfElse(ifelse) => ifelse.downgrade(ctx),
|
IfElse(ifelse) => ifelse.downgrade(ctx),
|
||||||
Select(select) => select.downgrade(ctx),
|
Select(select) => select.downgrade(ctx),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use rnix::TextRange;
|
|||||||
|
|
||||||
pub fn merge_spans(spans: impl IntoIterator<Item = TextRange>) -> TextRange {
|
pub fn merge_spans(spans: impl IntoIterator<Item = TextRange>) -> TextRange {
|
||||||
let mut spans = spans.into_iter();
|
let mut spans = spans.into_iter();
|
||||||
let first = spans.next().unwrap_or_else(|| synthetic_span());
|
let first = spans.next().unwrap_or_else(synthetic_span);
|
||||||
|
|
||||||
spans.fold(first, |acc, span| {
|
spans.fold(first, |acc, span| {
|
||||||
let start = acc.start().min(span.start());
|
let start = acc.start().min(span.start());
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
// Assume no parse error
|
// Assume no parse error
|
||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rnix::ast;
|
use rnix::ast;
|
||||||
use rowan::ast::AstNode;
|
use rowan::ast::AstNode;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ir::{Attr, AttrSet, ConcatStrings, ExprId, Ir, Select, Str, SymId};
|
use crate::ir::{Attr, AttrSet, ConcatStrings, ExprId, Select, Str, SymId};
|
||||||
use crate::value::format_symbol;
|
use crate::value::format_symbol;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -28,7 +26,7 @@ pub fn maybe_thunk(mut expr: ast::Expr, ctx: &mut impl DowngradeContext) -> Resu
|
|||||||
let span = error.syntax().text_range();
|
let span = error.syntax().text_range();
|
||||||
return Err(self::Error::downgrade_error(error.to_string())
|
return Err(self::Error::downgrade_error(error.to_string())
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
Ident(ident) => return ident.downgrade(ctx),
|
Ident(ident) => return ident.downgrade(ctx),
|
||||||
Literal(lit) => return lit.downgrade(ctx),
|
Literal(lit) => return lit.downgrade(ctx),
|
||||||
@@ -59,7 +57,7 @@ pub fn maybe_thunk(mut expr: ast::Expr, ctx: &mut impl DowngradeContext) -> Resu
|
|||||||
inner: id,
|
inner: id,
|
||||||
// span: ctx.get_span(id),
|
// span: ctx.get_span(id),
|
||||||
// FIXME: span
|
// FIXME: span
|
||||||
span: synthetic_span()
|
span: synthetic_span(),
|
||||||
}
|
}
|
||||||
.to_ir(),
|
.to_ir(),
|
||||||
))
|
))
|
||||||
@@ -138,7 +136,7 @@ pub fn downgrade_inherit(
|
|||||||
"dynamic attributes not allowed in inherit".to_string(),
|
"dynamic attributes not allowed in inherit".to_string(),
|
||||||
)
|
)
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let expr = if let Some(expr) = from {
|
let expr = if let Some(expr) = from {
|
||||||
@@ -168,7 +166,7 @@ pub fn downgrade_inherit(
|
|||||||
format_symbol(ctx.get_sym(*occupied.key()))
|
format_symbol(ctx.get_sym(*occupied.key()))
|
||||||
))
|
))
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
Entry::Vacant(vacant) => vacant.insert(expr),
|
Entry::Vacant(vacant) => vacant.insert(expr),
|
||||||
};
|
};
|
||||||
@@ -250,7 +248,7 @@ pub fn downgrade_static_attrpathvalue(
|
|||||||
"dynamic attributes not allowed in let bindings".to_string(),
|
"dynamic attributes not allowed in let bindings".to_string(),
|
||||||
)
|
)
|
||||||
.with_span(attrpath_node.syntax().text_range())
|
.with_span(attrpath_node.syntax().text_range())
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
let value = value.value().unwrap().downgrade(ctx)?;
|
let value = value.value().unwrap().downgrade(ctx)?;
|
||||||
attrs.insert(path, value, ctx)
|
attrs.insert(path, value, ctx)
|
||||||
@@ -298,7 +296,7 @@ where
|
|||||||
format_symbol(ctx.get_sym(sym))
|
format_symbol(ctx.get_sym(sym))
|
||||||
))
|
))
|
||||||
.with_span(span)
|
.with_span(span)
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let default_ast = entry.default();
|
let default_ast = entry.default();
|
||||||
@@ -439,7 +437,7 @@ where
|
|||||||
inner: expr,
|
inner: expr,
|
||||||
// span: ctx.get_span(expr),
|
// span: ctx.get_span(expr),
|
||||||
// FIXME: span
|
// FIXME: span
|
||||||
span: synthetic_span()
|
span: synthetic_span(),
|
||||||
}
|
}
|
||||||
.to_ir(),
|
.to_ir(),
|
||||||
);
|
);
|
||||||
@@ -449,7 +447,7 @@ where
|
|||||||
format_symbol(ctx.get_sym(sym))
|
format_symbol(ctx.get_sym(sym))
|
||||||
))
|
))
|
||||||
.with_span(synthetic_span())
|
.with_span(synthetic_span())
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +487,7 @@ where
|
|||||||
format_symbol(ctx.get_sym(sym))
|
format_symbol(ctx.get_sym(sym))
|
||||||
))
|
))
|
||||||
.with_span(ident.syntax().text_range())
|
.with_span(ident.syntax().text_range())
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,7 +507,7 @@ where
|
|||||||
format_symbol(ctx.get_sym(sym))
|
format_symbol(ctx.get_sym(sym))
|
||||||
))
|
))
|
||||||
.with_span(ident.syntax().text_range())
|
.with_span(ident.syntax().text_range())
|
||||||
.with_source(ctx.get_current_source().unwrap_or_else(|| Arc::from(""))));
|
.with_source(ctx.get_current_source().expect("no source set")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if attrs_vec.len() > 1 {
|
} else if attrs_vec.len() > 1 {
|
||||||
@@ -532,7 +530,7 @@ where
|
|||||||
let mut temp_attrs = AttrSet {
|
let mut temp_attrs = AttrSet {
|
||||||
stcs: HashMap::new(),
|
stcs: HashMap::new(),
|
||||||
dyns: Vec::new(),
|
dyns: Vec::new(),
|
||||||
span: synthetic_span()
|
span: synthetic_span(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ mod ir;
|
|||||||
mod nar;
|
mod nar;
|
||||||
mod nix_hash;
|
mod nix_hash;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
mod sourcemap;
|
|
||||||
mod store;
|
mod store;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use std::sync::{Arc, Once};
|
use std::sync::Once;
|
||||||
|
|
||||||
use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpState, RuntimeOptions, v8};
|
use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpState, RuntimeOptions, v8};
|
||||||
use deno_error::JsErrorClass;
|
use deno_error::JsErrorClass;
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result, Source};
|
||||||
use crate::value::{AttrSet, List, Symbol, Value};
|
use crate::value::{AttrSet, List, Symbol, Value};
|
||||||
|
|
||||||
type ScopeRef<'p, 's> = v8::PinnedRef<'p, v8::HandleScope<'s>>;
|
type ScopeRef<'p, 's> = v8::PinnedRef<'p, v8::HandleScope<'s>>;
|
||||||
@@ -15,9 +15,9 @@ type LocalSymbol<'a> = v8::Local<'a, v8::Symbol>;
|
|||||||
|
|
||||||
pub(crate) trait RuntimeContext: 'static {
|
pub(crate) trait RuntimeContext: 'static {
|
||||||
fn get_current_dir(&self) -> &Path;
|
fn get_current_dir(&self) -> &Path;
|
||||||
fn set_current_file(&mut self, path: PathBuf);
|
fn set_current_file(&mut self, path: Source);
|
||||||
fn compile_code(&mut self, code: &str) -> Result<String>;
|
fn compile_code(&mut self, source: Source) -> Result<String>;
|
||||||
fn get_current_source(&self) -> Option<Arc<str>>;
|
fn get_current_source(&self) -> Option<Source>;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runtime_extension<Ctx: RuntimeContext>() -> Extension {
|
fn runtime_extension<Ctx: RuntimeContext>() -> Extension {
|
||||||
@@ -96,13 +96,17 @@ fn op_import<Ctx: RuntimeContext>(
|
|||||||
|
|
||||||
tracing::info!("Importing file: {}", absolute_path.display());
|
tracing::info!("Importing file: {}", absolute_path.display());
|
||||||
|
|
||||||
let content = std::fs::read_to_string(&absolute_path)
|
let content = std::fs::read_to_string(absolute_path.as_path())
|
||||||
.map_err(|e| format!("Failed to read {}: {}", absolute_path.display(), e))?;
|
.map_err(|e| format!("Failed to read {}: {}", absolute_path.display(), e))?;
|
||||||
|
let source = Source {
|
||||||
|
ty: crate::error::SourceType::File(absolute_path.into()),
|
||||||
|
src: content.into(),
|
||||||
|
};
|
||||||
|
|
||||||
tracing::debug!("Compiling file");
|
tracing::debug!("Compiling file");
|
||||||
ctx.set_current_file(absolute_path);
|
ctx.set_current_file(source.clone());
|
||||||
|
|
||||||
Ok(ctx.compile_code(&content).map_err(|err| err.to_string())?)
|
Ok(ctx.compile_code(source).map_err(|err| err.to_string())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deno_core::op2]
|
#[deno_core::op2]
|
||||||
@@ -467,10 +471,10 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
|
|||||||
// Get current source from Context
|
// Get current source from Context
|
||||||
let op_state = self.js_runtime.op_state();
|
let op_state = self.js_runtime.op_state();
|
||||||
let op_state_borrow = op_state.borrow();
|
let op_state_borrow = op_state.borrow();
|
||||||
if let Some(ctx) = op_state_borrow.try_borrow::<Ctx>() {
|
if let Some(ctx) = op_state_borrow.try_borrow::<Ctx>()
|
||||||
if let Some(source) = ctx.get_current_source() {
|
&& let Some(source) = ctx.get_current_source()
|
||||||
error = error.with_source(source);
|
{
|
||||||
}
|
error = error.with_source(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
|
||||||
use rnix::TextRange;
|
|
||||||
use sourcemap::{SourceMap, SourceMapBuilder};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct NixSourceMapBuilder {
|
|
||||||
builder: SourceMapBuilder,
|
|
||||||
source_name: String,
|
|
||||||
source_content: Arc<str>,
|
|
||||||
generated_code: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NixSourceMapBuilder {
|
|
||||||
pub fn new(source_name: impl Into<String>, source_content: Arc<str>) -> Self {
|
|
||||||
let mut builder = SourceMapBuilder::new(None);
|
|
||||||
let source_name = source_name.into();
|
|
||||||
builder.add_source(&source_name);
|
|
||||||
builder.set_source_contents(0, Some(&source_content));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
builder,
|
|
||||||
source_name,
|
|
||||||
source_content,
|
|
||||||
generated_code: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_mapping(&mut self, js_offset: usize, nix_span: TextRange) {
|
|
||||||
let (js_line, js_col) = byte_to_line_col(&self.generated_code, js_offset);
|
|
||||||
let (nix_line, nix_col) = byte_to_line_col(&self.source_content, nix_span.start().into());
|
|
||||||
|
|
||||||
self.builder.add_raw(
|
|
||||||
js_line,
|
|
||||||
js_col,
|
|
||||||
nix_line,
|
|
||||||
nix_col,
|
|
||||||
Some(0),
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_generated_code(&mut self, code: String) {
|
|
||||||
self.generated_code = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self) -> Result<(SourceMap, String), sourcemap::Error> {
|
|
||||||
let sourcemap = self.builder.into_sourcemap();
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
sourcemap.to_writer(&mut buf)?;
|
|
||||||
|
|
||||||
let encoded = STANDARD.encode(&buf);
|
|
||||||
let data_url = format!(
|
|
||||||
"data:application/json;charset=utf-8;base64,{}",
|
|
||||||
encoded
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok((sourcemap, data_url))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn byte_to_line_col(text: &str, byte_offset: usize) -> (u32, u32) {
|
|
||||||
let mut line = 0;
|
|
||||||
let mut col = 0;
|
|
||||||
let mut current_offset = 0;
|
|
||||||
|
|
||||||
for ch in text.chars() {
|
|
||||||
if current_offset >= byte_offset {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ch == '\n' {
|
|
||||||
line += 1;
|
|
||||||
col = 0;
|
|
||||||
} else {
|
|
||||||
col += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_offset += ch.len_utf8();
|
|
||||||
}
|
|
||||||
|
|
||||||
(line, col)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_byte_to_line_col() {
|
|
||||||
let text = "line1\nline2\nline3";
|
|
||||||
|
|
||||||
assert_eq!(byte_to_line_col(text, 0), (0, 0));
|
|
||||||
assert_eq!(byte_to_line_col(text, 5), (0, 5));
|
|
||||||
assert_eq!(byte_to_line_col(text, 6), (1, 0));
|
|
||||||
assert_eq!(byte_to_line_col(text, 12), (2, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sourcemap_builder() {
|
|
||||||
let source = Arc::<str>::from("let x = 1; in x");
|
|
||||||
let mut builder = NixSourceMapBuilder::new("test.nix", source);
|
|
||||||
|
|
||||||
let span = TextRange::new(4.into(), 5.into());
|
|
||||||
builder.add_mapping(0, span);
|
|
||||||
|
|
||||||
let result = builder.build();
|
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -210,9 +210,7 @@ impl Store for DaemonStore {
|
|||||||
path: store_path,
|
path: store_path,
|
||||||
deriver: None,
|
deriver: None,
|
||||||
nar_hash: unsafe {
|
nar_hash: unsafe {
|
||||||
std::mem::transmute::<[u8; 32], nix_compat::nix_daemon::types::NarHash>(
|
std::mem::transmute::<[u8; 32], nix_compat::nix_daemon::types::NarHash>(nar_hash)
|
||||||
nar_hash,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
references: ref_store_paths,
|
references: ref_store_paths,
|
||||||
registration_time: 0,
|
registration_time: 0,
|
||||||
@@ -286,9 +284,7 @@ impl Store for DaemonStore {
|
|||||||
path: store_path,
|
path: store_path,
|
||||||
deriver: None,
|
deriver: None,
|
||||||
nar_hash: unsafe {
|
nar_hash: unsafe {
|
||||||
std::mem::transmute::<[u8; 32], nix_compat::nix_daemon::types::NarHash>(
|
std::mem::transmute::<[u8; 32], nix_compat::nix_daemon::types::NarHash>(nar_hash)
|
||||||
nar_hash,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
references: ref_store_paths,
|
references: ref_store_paths,
|
||||||
registration_time: 0,
|
registration_time: 0,
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use nix_js::context::Context;
|
use nix_js::context::Context;
|
||||||
|
use nix_js::error::Source;
|
||||||
use nix_js::value::Value;
|
use nix_js::value::Value;
|
||||||
|
|
||||||
|
use crate::utils::{eval, eval_result};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_absolute_path() {
|
fn import_absolute_path() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let lib_path = temp_dir.path().join("nix_test_lib.nix");
|
let lib_path = temp_dir.path().join("nix_test_lib.nix");
|
||||||
|
|
||||||
std::fs::write(&lib_path, "{ add = a: b: a + b; }").unwrap();
|
std::fs::write(&lib_path, "{ add = a: b: a + b; }").unwrap();
|
||||||
|
|
||||||
let expr = format!(r#"(import "{}").add 3 5"#, lib_path.display());
|
let expr = format!(r#"(import "{}").add 3 5"#, lib_path.display());
|
||||||
assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(8));
|
assert_eq!(eval(&expr), Value::Int(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_nested() {
|
fn import_nested() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
|
|
||||||
let lib_path = temp_dir.path().join("lib.nix");
|
let lib_path = temp_dir.path().join("lib.nix");
|
||||||
@@ -33,13 +32,11 @@ fn import_nested() {
|
|||||||
std::fs::write(&main_path, main_content).unwrap();
|
std::fs::write(&main_path, main_content).unwrap();
|
||||||
|
|
||||||
let expr = format!(r#"(import "{}").result"#, main_path.display());
|
let expr = format!(r#"(import "{}").result"#, main_path.display());
|
||||||
assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(30));
|
assert_eq!(eval(&expr), Value::Int(30));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_relative_path() {
|
fn import_relative_path() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let subdir = temp_dir.path().join("subdir");
|
let subdir = temp_dir.path().join("subdir");
|
||||||
std::fs::create_dir_all(&subdir).unwrap();
|
std::fs::create_dir_all(&subdir).unwrap();
|
||||||
@@ -63,27 +60,24 @@ fn import_relative_path() {
|
|||||||
std::fs::write(&main_path, main_content).unwrap();
|
std::fs::write(&main_path, main_content).unwrap();
|
||||||
|
|
||||||
let expr = format!(r#"let x = import "{}"; in x.result1"#, main_path.display());
|
let expr = format!(r#"let x = import "{}"; in x.result1"#, main_path.display());
|
||||||
assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(12));
|
assert_eq!(eval(&expr), Value::Int(12));
|
||||||
|
|
||||||
let expr = format!(r#"let x = import "{}"; in x.result2"#, main_path.display());
|
let expr = format!(r#"let x = import "{}"; in x.result2"#, main_path.display());
|
||||||
assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(7));
|
assert_eq!(eval(&expr), Value::Int(7));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_returns_function() {
|
fn import_returns_function() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let func_path = temp_dir.path().join("nix_test_func.nix");
|
let func_path = temp_dir.path().join("nix_test_func.nix");
|
||||||
std::fs::write(&func_path, "x: x * 2").unwrap();
|
std::fs::write(&func_path, "x: x * 2").unwrap();
|
||||||
|
|
||||||
let expr = format!(r#"(import "{}") 5"#, func_path.display());
|
let expr = format!(r#"(import "{}") 5"#, func_path.display());
|
||||||
assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(10));
|
assert_eq!(eval(&expr), Value::Int(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_with_complex_dependency_graph() {
|
fn import_with_complex_dependency_graph() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
|
|
||||||
let utils_path = temp_dir.path().join("utils.nix");
|
let utils_path = temp_dir.path().join("utils.nix");
|
||||||
@@ -98,7 +92,7 @@ fn import_with_complex_dependency_graph() {
|
|||||||
std::fs::write(&main_path, main_content).unwrap();
|
std::fs::write(&main_path, main_content).unwrap();
|
||||||
|
|
||||||
let expr = format!(r#"import "{}""#, main_path.display());
|
let expr = format!(r#"import "{}""#, main_path.display());
|
||||||
assert_eq!(ctx.eval_code(&expr).unwrap(), Value::Int(15));
|
assert_eq!(eval(&expr), Value::Int(15));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests for builtins.path
|
// Tests for builtins.path
|
||||||
@@ -111,7 +105,7 @@ fn path_with_file() {
|
|||||||
std::fs::write(&test_file, "Hello, World!").unwrap();
|
std::fs::write(&test_file, "Hello, World!").unwrap();
|
||||||
|
|
||||||
let expr = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
|
let expr = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
|
||||||
let result = ctx.eval_code(&expr).unwrap();
|
let result = ctx.eval_code(Source::new_eval(expr).unwrap()).unwrap();
|
||||||
|
|
||||||
// Should return a store path string
|
// Should return a store path string
|
||||||
if let Value::String(store_path) = result {
|
if let Value::String(store_path) = result {
|
||||||
@@ -124,7 +118,6 @@ fn path_with_file() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_with_custom_name() {
|
fn path_with_custom_name() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let test_file = temp_dir.path().join("original.txt");
|
let test_file = temp_dir.path().join("original.txt");
|
||||||
std::fs::write(&test_file, "Content").unwrap();
|
std::fs::write(&test_file, "Content").unwrap();
|
||||||
@@ -133,7 +126,7 @@ fn path_with_custom_name() {
|
|||||||
r#"builtins.path {{ path = {}; name = "custom-name"; }}"#,
|
r#"builtins.path {{ path = {}; name = "custom-name"; }}"#,
|
||||||
test_file.display()
|
test_file.display()
|
||||||
);
|
);
|
||||||
let result = ctx.eval_code(&expr).unwrap();
|
let result = eval(&expr);
|
||||||
|
|
||||||
if let Value::String(store_path) = result {
|
if let Value::String(store_path) = result {
|
||||||
assert!(store_path.contains("custom-name"));
|
assert!(store_path.contains("custom-name"));
|
||||||
@@ -156,7 +149,7 @@ fn path_with_directory_recursive() {
|
|||||||
r#"builtins.path {{ path = {}; recursive = true; }}"#,
|
r#"builtins.path {{ path = {}; recursive = true; }}"#,
|
||||||
test_dir.display()
|
test_dir.display()
|
||||||
);
|
);
|
||||||
let result = ctx.eval_code(&expr).unwrap();
|
let result = ctx.eval_code(Source::new_eval(expr).unwrap()).unwrap();
|
||||||
|
|
||||||
if let Value::String(store_path) = result {
|
if let Value::String(store_path) = result {
|
||||||
assert!(store_path.starts_with(ctx.get_store_dir()));
|
assert!(store_path.starts_with(ctx.get_store_dir()));
|
||||||
@@ -177,7 +170,7 @@ fn path_flat_with_file() {
|
|||||||
r#"builtins.path {{ path = {}; recursive = false; }}"#,
|
r#"builtins.path {{ path = {}; recursive = false; }}"#,
|
||||||
test_file.display()
|
test_file.display()
|
||||||
);
|
);
|
||||||
let result = ctx.eval_code(&expr).unwrap();
|
let result = ctx.eval_code(Source::new_eval(expr).unwrap()).unwrap();
|
||||||
|
|
||||||
if let Value::String(store_path) = result {
|
if let Value::String(store_path) = result {
|
||||||
assert!(store_path.starts_with(ctx.get_store_dir()));
|
assert!(store_path.starts_with(ctx.get_store_dir()));
|
||||||
@@ -188,7 +181,6 @@ fn path_flat_with_file() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_flat_with_directory_fails() {
|
fn path_flat_with_directory_fails() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let test_dir = temp_dir.path().join("mydir");
|
let test_dir = temp_dir.path().join("mydir");
|
||||||
std::fs::create_dir_all(&test_dir).unwrap();
|
std::fs::create_dir_all(&test_dir).unwrap();
|
||||||
@@ -197,7 +189,7 @@ fn path_flat_with_directory_fails() {
|
|||||||
r#"builtins.path {{ path = {}; recursive = false; }}"#,
|
r#"builtins.path {{ path = {}; recursive = false; }}"#,
|
||||||
test_dir.display()
|
test_dir.display()
|
||||||
);
|
);
|
||||||
let result = ctx.eval_code(&expr);
|
let result = eval_result(&expr);
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
let err_msg = result.unwrap_err().to_string();
|
let err_msg = result.unwrap_err().to_string();
|
||||||
@@ -206,10 +198,8 @@ fn path_flat_with_directory_fails() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_nonexistent_fails() {
|
fn path_nonexistent_fails() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
|
|
||||||
let expr = r#"builtins.path { path = "/nonexistent/path/that/should/not/exist"; }"#;
|
let expr = r#"builtins.path { path = "/nonexistent/path/that/should/not/exist"; }"#;
|
||||||
let result = ctx.eval_code(expr);
|
let result = eval_result(expr);
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
let err_msg = result.unwrap_err().to_string();
|
let err_msg = result.unwrap_err().to_string();
|
||||||
@@ -218,10 +208,8 @@ fn path_nonexistent_fails() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_missing_path_param() {
|
fn path_missing_path_param() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
|
|
||||||
let expr = r#"builtins.path { name = "test"; }"#;
|
let expr = r#"builtins.path { name = "test"; }"#;
|
||||||
let result = ctx.eval_code(expr);
|
let result = eval_result(expr);
|
||||||
|
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
let err_msg = result.unwrap_err().to_string();
|
let err_msg = result.unwrap_err().to_string();
|
||||||
@@ -230,14 +218,13 @@ fn path_missing_path_param() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_with_sha256() {
|
fn path_with_sha256() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let test_file = temp_dir.path().join("hash_test.txt");
|
let test_file = temp_dir.path().join("hash_test.txt");
|
||||||
std::fs::write(&test_file, "Test content for hashing").unwrap();
|
std::fs::write(&test_file, "Test content for hashing").unwrap();
|
||||||
|
|
||||||
// First, get the hash by calling without sha256
|
// First, get the hash by calling without sha256
|
||||||
let expr1 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
|
let expr1 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
|
||||||
let result1 = ctx.eval_code(&expr1).unwrap();
|
let result1 = eval(&expr1);
|
||||||
let store_path1 = match result1 {
|
let store_path1 = match result1 {
|
||||||
Value::String(s) => s,
|
Value::String(s) => s,
|
||||||
_ => panic!("Expected string"),
|
_ => panic!("Expected string"),
|
||||||
@@ -246,7 +233,7 @@ fn path_with_sha256() {
|
|||||||
// Compute the actual hash (for testing, we'll just verify the same path is returned)
|
// Compute the actual hash (for testing, we'll just verify the same path is returned)
|
||||||
// In real usage, the user would know the hash beforehand
|
// In real usage, the user would know the hash beforehand
|
||||||
let expr2 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
|
let expr2 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
|
||||||
let result2 = ctx.eval_code(&expr2).unwrap();
|
let result2 = eval(&expr2);
|
||||||
let store_path2 = match result2 {
|
let store_path2 = match result2 {
|
||||||
Value::String(s) => s,
|
Value::String(s) => s,
|
||||||
_ => panic!("Expected string"),
|
_ => panic!("Expected string"),
|
||||||
@@ -258,7 +245,6 @@ fn path_with_sha256() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn path_deterministic() {
|
fn path_deterministic() {
|
||||||
let mut ctx = Context::new().unwrap();
|
|
||||||
let temp_dir = tempfile::tempdir().unwrap();
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
let test_file = temp_dir.path().join("deterministic.txt");
|
let test_file = temp_dir.path().join("deterministic.txt");
|
||||||
std::fs::write(&test_file, "Same content").unwrap();
|
std::fs::write(&test_file, "Same content").unwrap();
|
||||||
@@ -268,8 +254,8 @@ fn path_deterministic() {
|
|||||||
test_file.display()
|
test_file.display()
|
||||||
);
|
);
|
||||||
|
|
||||||
let result1 = ctx.eval_code(&expr).unwrap();
|
let result1 = eval(&expr);
|
||||||
let result2 = ctx.eval_code(&expr).unwrap();
|
let result2 = eval(&expr);
|
||||||
|
|
||||||
// Same inputs should produce same store path
|
// Same inputs should produce same store path
|
||||||
assert_eq!(result1, result2);
|
assert_eq!(result1, result2);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
mod utils;
|
||||||
|
|
||||||
use nix_js::context::Context;
|
use nix_js::context::Context;
|
||||||
use nix_js::value::Value;
|
use nix_js::value::Value;
|
||||||
|
use utils::eval_result;
|
||||||
|
|
||||||
fn eval(expr: &str) -> Value {
|
fn eval(expr: &str) -> Value {
|
||||||
let mut ctx = Context::new().unwrap();
|
let mut ctx = Context::new().unwrap();
|
||||||
ctx.eval_code(expr).unwrap_or_else(|e| panic!("{}", e))
|
eval_result(expr).unwrap_or_else(|e| panic!("{}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -165,7 +168,9 @@ fn context_in_derivation_args() {
|
|||||||
args = [ ((builtins.toString dep) + "/bin/run") ];
|
args = [ ((builtins.toString dep) + "/bin/run") ];
|
||||||
};
|
};
|
||||||
in drv.drvPath
|
in drv.drvPath
|
||||||
"#,
|
"#
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match result {
|
match result {
|
||||||
@@ -192,7 +197,9 @@ fn context_in_derivation_env() {
|
|||||||
myDep = builtins.toString dep;
|
myDep = builtins.toString dep;
|
||||||
};
|
};
|
||||||
in drv.drvPath
|
in drv.drvPath
|
||||||
"#,
|
"#
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match result {
|
match result {
|
||||||
@@ -226,7 +233,9 @@ fn interpolation_derivation_returns_outpath() {
|
|||||||
let
|
let
|
||||||
drv = derivation { name = "test"; builder = "/bin/sh"; system = "x86_64-linux"; };
|
drv = derivation { name = "test"; builder = "/bin/sh"; system = "x86_64-linux"; };
|
||||||
in "${drv}"
|
in "${drv}"
|
||||||
"#,
|
"#
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use nix_js::context::Context;
|
use nix_js::context::Context;
|
||||||
|
use nix_js::error::Source;
|
||||||
use nix_js::value::Value;
|
use nix_js::value::Value;
|
||||||
|
|
||||||
pub fn eval(expr: &str) -> Value {
|
pub fn eval(expr: &str) -> Value {
|
||||||
Context::new().unwrap().eval_code(expr).unwrap()
|
Context::new()
|
||||||
|
.unwrap()
|
||||||
|
.eval_code(Source::new_eval(expr.into()).unwrap())
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_result(expr: &str) -> Result<Value, nix_js::error::Error> {
|
pub fn eval_result(expr: &str) -> Result<Value, nix_js::error::Error> {
|
||||||
Context::new().unwrap().eval_code(expr)
|
Context::new()
|
||||||
|
.unwrap()
|
||||||
|
.eval_code(Source::new_eval(expr.into()).unwrap())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user