git.delta.rocks / jrsonnet / refs/commits / 772afa0049b2

difftreelog

refactor saner imports from TLA/std.extVars

tkuxywosYaroslav Bolyukin2026-02-07parent: #66a4ff8.patch.diff
in: master

13 files changed

modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
14use jrsonnet_evaluator::{14use jrsonnet_evaluator::{
15 bail,15 bail,
16 error::{ErrorKind::*, Result},16 error::{ErrorKind::*, Result},
17 ImportResolver,17 AsPathLike, ImportResolver, ResolvePath,
18};18};
19use jrsonnet_gcmodule::Acyclic;19use jrsonnet_gcmodule::Acyclic;
20use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};20use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};
38 out: RefCell<HashMap<SourcePath, Vec<u8>>>,38 out: RefCell<HashMap<SourcePath, Vec<u8>>>,
39}39}
40impl ImportResolver for CallbackImportResolver {40impl ImportResolver for CallbackImportResolver {
41 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {41 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {
42 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {42 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {
43 let mut o = p.path().to_owned();43 let mut o = p.path().to_owned();
44 o.pop();44 o.pop();
51 unreachable!("can't resolve this path");51 unreachable!("can't resolve this path");
52 };52 };
53 let base = unsafe { crate::unparse_path(&base) };53 let base = unsafe { crate::unparse_path(&base) };
54 let rel = path.as_path();
54 let rel = CString::new(path).unwrap();55 let rel = match rel {
56 ResolvePath::Str(s) => CString::new(s.as_bytes()).unwrap(),
57 ResolvePath::Path(p) => unsafe { crate::unparse_path(p) },
58 };
55 let found_here: *mut c_char = null_mut();59 let found_here: *mut c_char = null_mut();
5660
57 let mut buf = null_mut();61 let mut buf = null_mut();
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
28 rustc_hash::FxHashMap,28 rustc_hash::FxHashMap,
29 stack::set_stack_depth_limit,29 stack::set_stack_depth_limit,
30 trace::{CompactFormat, PathResolver, TraceFormat},30 trace::{CompactFormat, PathResolver, TraceFormat},
31 FileImportResolver, IStr, ImportResolver, Result, State, Val,31 AsPathLike, FileImportResolver, IStr, ImportResolver, Result, State, Val,
32};32};
33use jrsonnet_gcmodule::Acyclic;33use jrsonnet_gcmodule::Acyclic;
34use jrsonnet_parser::SourcePath;34use jrsonnet_parser::SourcePath;
62 }62 }
63}63}
6464
65unsafe fn unparse_path(input: &Path) -> Cow<'_, CStr> {65unsafe fn unparse_path(input: &Path) -> CString {
66 #[cfg(target_family = "unix")]66 #[cfg(target_family = "unix")]
67 {67 {
68 use std::os::unix::ffi::OsStrExt;68 use std::os::unix::ffi::OsStrExt;
69 let str = CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it");69 let str = CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it");
70 Cow::Owned(str)70 str
71 }71 }
72 #[cfg(not(target_family = "unix"))]72 #[cfg(not(target_family = "unix"))]
73 {73 {
74 let str = input.as_os_str().to_str().expect("bad utf-8");74 let str = input.as_os_str().to_str().expect("bad utf-8");
75 let cstr = CString::new(str).expect("input has NUL inside");75 let cstr = CString::new(str).expect("input has NUL inside");
76 Cow::Owned(cstr)76 cstr
77 }77 }
78}78}
7979
93 self.inner.borrow().load_file_contents(resolved)93 self.inner.borrow().load_file_contents(resolved)
94 }94 }
9595
96 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {96 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {
97 self.inner.borrow().resolve_from(from, path)97 self.inner.borrow().resolve_from(from, path)
98 }98 }
9999
100 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {100 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {
101 self.inner.borrow().resolve_from_default(path)101 self.inner.borrow().resolve_from_default(path)
102 }102 }
103
104 fn resolve(&self, path: &Path) -> Result<SourcePath> {
105 self.inner.borrow().resolve(path)
106 }
107}103}
108104
109pub struct VM {105pub struct VM {
modifiedbindings/jsonnet/src/vars_tlas.rsdiffbeforeafterboth
3use std::{ffi::CStr, os::raw::c_char};3use std::{ffi::CStr, os::raw::c_char};
44
5use jrsonnet_evaluator::{function::TlaArg, IStr};5use jrsonnet_evaluator::{function::TlaArg, IStr};
6use jrsonnet_parser::{ParserSettings, Source};
76
8use crate::VM;7use crate::VM;
98
84 let code = unsafe { CStr::from_ptr(code) };83 let code = unsafe { CStr::from_ptr(code) };
8584
86 let name: IStr = name.to_str().expect("name is not utf-8").into();85 let name: IStr = name.to_str().expect("name is not utf-8").into();
87 let code: IStr = code.to_str().expect("code is not utf-8").into();86 let code: String = code.to_str().expect("code is not utf-8").to_owned();
88 let code = jrsonnet_parser::parse(
89 &code,
90 &ParserSettings {
91 source: Source::new_virtual(format!("<top-level-arg:{name}>").into(), code.clone()),
92 },
93 )
94 .expect("can't parse TLA code");
9587
96 vm.tla_args.insert(name, TlaArg::Code(code));88 vm.tla_args.insert(name, TlaArg::InlineCode(code));
97}89}
9890
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
182 let input_str = std::str::from_utf8(&input)?;182 let input_str = std::str::from_utf8(&input)?;
183 s.evaluate_snippet("<stdin>".to_owned(), input_str)?183 s.evaluate_snippet("<stdin>".to_owned(), input_str)?
184 } else {184 } else {
185 s.import(&input)?185 s.import(input.as_str())?
186 };186 };
187187
188 let tla = opts.tla.tla_opts()?;188 let tla = opts.tla.tla_opts()?;
modifiedcrates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth
1use std::{fs::read_to_string, str::FromStr};1use std::str::FromStr;
22
3use clap::Parser;3use clap::Parser;
4use jrsonnet_evaluator::{trace::PathResolver, Result};4use jrsonnet_evaluator::{function::TlaArg, trace::PathResolver, Result};
5use jrsonnet_stdlib::ContextInitializer;5use jrsonnet_stdlib::ContextInitializer;
66
7#[derive(Clone)]7#[derive(Clone)]
54#[derive(Clone)]54#[derive(Clone)]
55pub struct ExtFile {55pub struct ExtFile {
56 pub name: String,56 pub name: String,
57 pub value: String,57 pub path: String,
58}58}
5959
60impl FromStr for ExtFile {60impl FromStr for ExtFile {
61 type Err = String;61 type Err = String;
6262
63 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {63 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
64 let out: Vec<&str> = s.split('=').collect();64 let Some((name, path)) = s.split_once('=') else {
65 if out.len() != 2 {
66 return Err("bad ext-file syntax".to_owned());65 return Err("bad ext-file syntax".to_owned());
67 }66 };
68 let file = read_to_string(out[1]);
69 match file {
70 Ok(content) => Ok(Self {67 Ok(Self {
71 name: out[0].into(),68 name: name.into(),
72 value: content,69 path: path.into(),
73 }),70 })
74 Err(e) => Err(format!("{e}")),
75 }
76 }71 }
77}72}
7873
110 }105 }
111 let ctx = ContextInitializer::new(PathResolver::new_cwd_fallback());106 let ctx = ContextInitializer::new(PathResolver::new_cwd_fallback());
112 for ext in &self.ext_str {107 for ext in &self.ext_str {
113 ctx.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());108 ctx.settings_mut().ext_vars.insert(
109 ext.name.as_str().into(),
110 TlaArg::String(ext.value.as_str().into()),
111 );
114 }112 }
115 for ext in &self.ext_str_file {113 for ext in &self.ext_str_file {
116 ctx.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());114 ctx.settings_mut().ext_vars.insert(
115 ext.name.as_str().into(),
116 TlaArg::ImportStr(ext.path.clone()),
117 );
117 }118 }
118 for ext in &self.ext_code {119 for ext in &self.ext_code {
119 ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?;120 ctx.settings_mut().ext_vars.insert(
121 ext.name.as_str().into(),
122 TlaArg::InlineCode(ext.value.clone()),
123 );
120 }124 }
121 for ext in &self.ext_code_file {125 for ext in &self.ext_code_file {
126 ctx.settings_mut()
127 .ext_vars
122 ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?;128 .insert(ext.name.as_str().into(), TlaArg::Import(ext.path.clone()));
123 }129 }
124 Ok(Some(ctx))130 Ok(Some(ctx))
125 }131 }
modifiedcrates/jrsonnet-cli/src/tla.rsdiffbeforeafterboth
1use clap::Parser;1use clap::Parser;
2use jrsonnet_evaluator::{2use jrsonnet_evaluator::{IStr, error::Result, function::TlaArg, gc::WithCapacityExt as _, rustc_hash::FxHashMap};
3 error::{ErrorKind, Result},
4 function::TlaArg,
5 gc::WithCapacityExt as _,
6 rustc_hash::FxHashMap,
7 IStr,
8};
9use jrsonnet_parser::{ParserSettings, Source};
103
11use crate::{ExtFile, ExtStr};4use crate::{ExtFile, ExtStr};
125
35impl TlaOpts {28impl TlaOpts {
36 pub fn tla_opts(&self) -> Result<FxHashMap<IStr, TlaArg>> {29 pub fn tla_opts(&self) -> Result<FxHashMap<IStr, TlaArg>> {
37 let mut out = FxHashMap::new();30 let mut out = FxHashMap::new();
38 for (name, value) in self31 for ext in &self.tla_str {
39 .tla_str
40 .iter()
41 .map(|c| (&c.name, &c.value))
42 .chain(self.tla_str_file.iter().map(|c| (&c.name, &c.value)))
43 {
44 out.insert(name.into(), TlaArg::String(value.into()));32 out.insert(
33 ext.name.as_str().into(),
34 TlaArg::String(ext.value.as_str().into()),
35 );
45 }36 }
37 for ext in &self.tla_str_file {
38 out.insert(
39 ext.name.as_str().into(),
40 TlaArg::ImportStr(ext.name.as_str().into()),
41 );
42 }
46 for (name, code) in self43 for ext in &self.tla_code {
47 .tla_code
48 .iter()
49 .map(|c| (&c.name, &c.value))
50 .chain(self.tla_code_file.iter().map(|c| (&c.name, &c.value)))44 out.insert(
51 {45 ext.name.as_str().into(),
52 let source = Source::new_virtual(format!("<top-level-arg:{name}>").into(), code.into());46 TlaArg::InlineCode(ext.value.clone()),
47 );
48 }
49 for ext in &self.tla_code_file {
53 out.insert(50 out.insert(ext.name.as_str().into(), TlaArg::Import(ext.path.clone()));
54 (name as &str).into(),51 }
55 TlaArg::Code(
56 jrsonnet_parser::parse(
57 code,
58 &ParserSettings {
59 source: source.clone(),
60 },
61 )
62 .map_err(|e| ErrorKind::ImportSyntaxError {
63 path: source,
64 error: Box::new(e),
65 })?,
66 ),
67 );
68 }
69 Ok(out)52 Ok(out)
70 }53 }
71}54}
modifiedcrates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth
1use std::{any::Any, cell::RefCell, future::Future, path::Path};1use std::{any::Any, cell::RefCell, future::Future};
22
3use jrsonnet_gcmodule::Acyclic;3use jrsonnet_gcmodule::Acyclic;
4use jrsonnet_interner::IStr;
5use jrsonnet_parser::{4use jrsonnet_parser::{
6 ArgsDesc, AssertStmt, BindSpec, CompSpec, Destruct, Expr, FieldMember, FieldName, ForSpecData,5 ArgsDesc, AssertStmt, BindSpec, CompSpec, Destruct, Expr, FieldMember, FieldName, ForSpecData,
7 IfSpecData, LocExpr, Member, ObjBody, Param, ParamsDesc, ParserSettings, SliceDesc, Source,6 IfSpecData, LocExpr, Member, ObjBody, Param, ParamsDesc, ParserSettings, SliceDesc, Source,
8 SourcePath,7 SourcePath,
9};8};
10use rustc_hash::FxHashMap;9use rustc_hash::FxHashMap;
1110
12use crate::{bail, FileData, ImportResolver, State};11use crate::{AsPathLike, FileData, ImportResolver, ResolvePathOwned, State};
1312
14pub struct Import {13pub struct Import {
15 path: IStr,14 path: ResolvePathOwned,
16 expression: bool,15 expression: bool,
17}16}
1817
137 Expr::Import(v) | Expr::ImportStr(v) | Expr::ImportBin(v) => {136 Expr::Import(v) | Expr::ImportStr(v) | Expr::ImportBin(v) => {
138 if let Expr::Str(s) = &*v.expr() {137 if let Expr::Str(s) = &*v.expr() {
139 out.0.push(Import {138 out.0.push(Import {
140 path: s.clone(),139 path: ResolvePathOwned::Str(s.to_string()),
141 expression: matches!(&*expr.expr(), Expr::Import(_)),140 expression: matches!(&*expr.expr(), Expr::Import(_)),
142 });141 });
143 }142 }
229 fn resolve_from(228 fn resolve_from(
230 &self,229 &self,
231 from: &SourcePath,230 from: &SourcePath,
232 path: &str,231 path: &dyn AsPathLike,
233 ) -> impl Future<Output = Result<SourcePath, Self::Error>>;232 ) -> impl Future<Output = Result<SourcePath, Self::Error>>;
234 fn resolve_from_default(233 fn resolve_from_default(
235 &self,234 &self,
236 path: &str,235 path: &dyn AsPathLike,
237 ) -> impl Future<Output = Result<SourcePath, Self::Error>> {236 ) -> impl Future<Output = Result<SourcePath, Self::Error>> {
238 async { self.resolve_from(&SourcePath::default(), path).await }237 async { self.resolve_from(&SourcePath::default(), path).await }
239 }238 }
240 /// Resolves absolute path, doesn't supports jpath and other fancy things
241 fn resolve(&self, path: &Path) -> impl Future<Output = Result<SourcePath, Self::Error>>;
242239
243 /// Load resolved file240 /// Load resolved file
244 /// This should only be called with value returned241 /// This should only be called with value returned
253250
254#[derive(Acyclic)]251#[derive(Acyclic)]
255struct ResolvedImportResolver {252struct ResolvedImportResolver {
256 resolved: RefCell<FxHashMap<(SourcePath, IStr), (SourcePath, bool)>>,253 resolved: RefCell<FxHashMap<(SourcePath, ResolvePathOwned), (SourcePath, bool)>>,
257}254}
258impl ImportResolver for ResolvedImportResolver {255impl ImportResolver for ResolvedImportResolver {
259 fn load_file_contents(&self, _resolved: &SourcePath) -> crate::Result<Vec<u8>> {256 fn load_file_contents(&self, _resolved: &SourcePath) -> crate::Result<Vec<u8>> {
260 unreachable!("all files should be loaded at this point");257 unreachable!("all files should be loaded at this point");
261 }258 }
262259
263 fn resolve_from(&self, from: &SourcePath, path: &str) -> crate::Result<SourcePath> {260 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> crate::Result<SourcePath> {
264 Ok(self261 Ok(self
265 .resolved262 .resolved
266 .borrow()263 .borrow()
267 .get(&(from.clone(), path.into()))264 .get(&(from.clone(), path.as_path().to_owned()))
268 .expect("all imports should be resolved at this point")265 .expect("all imports should be resolved at this point")
269 .0266 .0
270 .clone())267 .clone())
271 }268 }
272269
273 fn resolve_from_default(&self, path: &str) -> crate::Result<SourcePath> {270 fn resolve_from_default(&self, path: &dyn AsPathLike) -> crate::Result<SourcePath> {
274 self.resolve_from(&SourcePath::default(), path)271 self.resolve_from(&SourcePath::default(), path)
275 }272 }
276
277 fn resolve(&self, path: &Path) -> crate::Result<SourcePath> {
278 bail!(crate::error::ErrorKind::AbsoluteImportNotSupported(
279 path.to_owned()
280 ))
281 }
282}273}
283274
284enum Job {275enum Job {
288}279}
289280
290#[allow(clippy::future_not_send)]281#[allow(clippy::future_not_send)]
291pub async fn async_import<H>(s: State, handler: H, path: impl AsRef<Path>) -> Result<(), H::Error>282pub async fn async_import<H>(s: State, handler: H, path: &dyn AsPathLike) -> Result<(), H::Error>
292where283where
293 H: AsyncImportResolver,284 H: AsyncImportResolver,
294{285{
299 let mut resolved_map = resolved.resolved.borrow_mut();290 let mut resolved_map = resolved.resolved.borrow_mut();
300291
301 let mut queue = vec![Job::LoadFile {292 let mut queue = vec![Job::LoadFile {
302 path: handler.resolve(path.as_ref()).await?,293 path: handler.resolve_from_default(path).await?,
303 parse: true,294 parse: true,
304 }];295 }];
305 while let Some(job) = queue.pop() {296 while let Some(job) = queue.pop() {
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
2 cmp::Ordering,2 cmp::Ordering,
3 convert::Infallible,3 convert::Infallible,
4 fmt::{Debug, Display},4 fmt::{Debug, Display},
5 path::PathBuf,
6};5};
76
8use jrsonnet_gcmodule::Trace;7use jrsonnet_gcmodule::Trace;
16 stdlib::format::FormatError,15 stdlib::format::FormatError,
17 typed::TypeLocError,16 typed::TypeLocError,
18 val::ConvertNumValueError,17 val::ConvertNumValueError,
19 ObjValue,18 ObjValue, ResolvePathOwned,
20};19};
2120
22pub(crate) fn format_found(list: &[IStr], what: &str) -> String {21pub(crate) fn format_found(list: &[IStr], what: &str) -> String {
180 StandaloneSuper,179 StandaloneSuper,
181180
182 #[error("can't resolve {1} from {0}")]181 #[error("can't resolve {1} from {0}")]
183 ImportFileNotFound(SourcePath, String),182 ImportFileNotFound(SourcePath, ResolvePathOwned),
184 #[error("can't resolve absolute {0}")]
185 AbsoluteImportFileNotFound(PathBuf),
186 #[error("resolved file not found: {:?}", .0)]183 #[error("resolved file not found: {:?}", .0)]
187 ResolvedFileNotFound(SourcePath),184 ResolvedFileNotFound(SourcePath),
188 #[error("can't import {0}: is a directory")]185 #[error("can't import {0}: is a directory")]
192 #[error("import io error: {0}")]189 #[error("import io error: {0}")]
193 ImportIo(String),190 ImportIo(String),
194 #[error("tried to import {1} from {0}, but imports are not supported")]191 #[error("tried to import {1} from {0}, but imports are not supported")]
195 ImportNotSupported(SourcePath, String),192 ImportNotSupported(SourcePath, ResolvePathOwned),
196 #[error("tried to import {0}, but absolute imports are not supported")]
197 AbsoluteImportNotSupported(PathBuf),
198 #[error("can't import from virtual file")]193 #[error("can't import from virtual file")]
199 CantImportFromVirtualFile,194 CantImportFromVirtualFile,
200 #[error(195 #[error(
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
682 };682 };
683 let tmp = loc.clone().0;683 let tmp = loc.clone().0;
684 let s = ctx.state();684 let s = ctx.state();
685 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;685 let resolved_path = s.resolve_from(tmp.source_path(), path)?;
686 match i {686 match i {
687 Import(_) => in_frame(687 Import(_) => in_frame(
688 CallLocation::new(&loc),688 CallLocation::new(&loc),
modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
22
3use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;
4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;
5use jrsonnet_parser::{ArgsDesc, LocExpr};5use jrsonnet_parser::{ArgsDesc, LocExpr, SourceFifo, SourcePath};
66
7use crate::{evaluate, typed::Typed, Context, Result, Thunk, Val};7use crate::{evaluate, typed::Typed, Context, Result, Thunk, Val};
88
41#[derive(Clone, Trace)]41#[derive(Clone, Trace)]
42pub enum TlaArg {42pub enum TlaArg {
43 String(IStr),43 String(IStr),
44 Code(LocExpr),
45 Val(Val),44 Val(Val),
46 Lazy(Thunk<Val>),45 Lazy(Thunk<Val>),
46 Import(String),
47 ImportStr(String),
48 InlineCode(String),
47}49}
48impl ArgLike for TlaArg {50impl ArgLike for TlaArg {
49 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {51 fn evaluate_arg(&self, ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {
50 match self {52 match self {
51 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),53 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),
54 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),
55 Self::Lazy(lazy) => Ok(lazy.clone()),
52 Self::Code(code) => Ok(if tailstrict {56 Self::Import(p) => {
53 Thunk::evaluated(evaluate(ctx, code)?)
54 } else {
55 let code = code.clone();57 let resolved = ctx.state().resolve_from_default(&p.as_str())?;
56 Thunk!(move || evaluate(ctx, &code))58 Ok(Thunk!(move || ctx.state().import_resolved(resolved)))
57 }),59 }
58 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),60 Self::ImportStr(p) => {
61 let resolved = ctx.state().resolve_from_default(&p.as_str())?;
62 Ok(Thunk!(move || ctx
63 .state()
64 .import_resolved_str(resolved)
65 .map(Val::string)))
66 }
59 Self::Lazy(lazy) => Ok(lazy.clone()),67 Self::InlineCode(p) => {
68 let resolved =
69 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));
70 Ok(Thunk!(move || ctx.state().import_resolved(resolved)))
71 }
60 }72 }
61 }73 }
62}74}
modifiedcrates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth
1use std::{1use std::{
2 any::Any,2 any::Any,
3 borrow::Cow,
3 env::current_dir,4 env::current_dir,
4 fs,5 fmt, fs,
5 io::{ErrorKind, Read},6 io::{ErrorKind, Read},
6 path::{Path, PathBuf},7 path::{Path, PathBuf},
7};8};
89
9use fs::File;10use fs::File;
10use jrsonnet_gcmodule::Acyclic;11use jrsonnet_gcmodule::Acyclic;
11use jrsonnet_interner::IBytes;12use jrsonnet_interner::IBytes;
12use jrsonnet_parser::{SourceDirectory, SourceFifo, SourceFile, SourcePath};13use jrsonnet_parser::{IStr, SourceDirectory, SourceFifo, SourceFile, SourcePath};
1314
14use crate::{15use crate::{
15 bail,16 bail,
16 error::{ErrorKind::*, Result},17 error::{ErrorKind::*, Result},
17};18};
19#[derive(Clone, Debug, Acyclic, Eq, Hash, PartialEq)]
20pub enum ResolvePathOwned {
21 Str(String),
22 Path(PathBuf),
23}
24impl fmt::Display for ResolvePathOwned {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 ResolvePathOwned::Str(s) => write!(f, "{s}"),
28 ResolvePathOwned::Path(p) => write!(f, "{}", p.display()),
29 }
30 }
31}
32#[derive(Clone, Copy)]
33pub enum ResolvePath<'s> {
34 Str(&'s str),
35 Path(&'s Path),
36}
37impl ResolvePath<'_> {
38 pub fn to_owned(self) -> ResolvePathOwned {
39 match self {
40 ResolvePath::Str(s) => ResolvePathOwned::Str(s.to_owned()),
41 ResolvePath::Path(p) => ResolvePathOwned::Path(p.to_owned()),
42 }
43 }
44}
45impl AsRef<Path> for ResolvePath<'_> {
46 fn as_ref(&self) -> &Path {
47 match self {
48 ResolvePath::Str(s) => s.as_ref(),
49 ResolvePath::Path(p) => p,
50 }
51 }
52}
53pub trait AsPathLike {
54 fn as_path(&self) -> ResolvePath<'_>;
55}
56impl<T> AsPathLike for &T
57where
58 T: AsPathLike + ?Sized,
59{
60 fn as_path(&self) -> ResolvePath<'_> {
61 (*self).as_path()
62 }
63}
64impl AsPathLike for str {
65 fn as_path(&self) -> ResolvePath<'_> {
66 ResolvePath::Str(self)
67 }
68}
69impl AsPathLike for IStr {
70 fn as_path(&self) -> ResolvePath<'_> {
71 ResolvePath::Str(self)
72 }
73}
74impl AsPathLike for Cow<'_, Path> {
75 fn as_path(&self) -> ResolvePath<'_> {
76 ResolvePath::Path(self.as_ref())
77 }
78}
79impl AsPathLike for Path {
80 fn as_path(&self) -> ResolvePath<'_> {
81 ResolvePath::Path(self)
82 }
83}
84impl AsPathLike for ResolvePathOwned {
85 fn as_path(&self) -> ResolvePath<'_> {
86 match self {
87 ResolvePathOwned::Str(s) => ResolvePath::Str(s),
88 ResolvePathOwned::Path(path_buf) => ResolvePath::Path(path_buf),
89 }
90 }
91}
1892
19/// Implements file resolution logic for `import` and `importStr`93/// Implements file resolution logic for `import` and `importStr`
20pub trait ImportResolver: Acyclic + Any {94pub trait ImportResolver: Acyclic + Any {
24 ///98 ///
25 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value99 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value
26 /// may result in panic100 /// may result in panic
27 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {101 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {
28 bail!(ImportNotSupported(from.clone(), path.into()))102 bail!(ImportNotSupported(from.clone(), path.as_path().to_owned()))
29 }103 }
30 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {104 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {
31 self.resolve_from(&SourcePath::default(), path)105 self.resolve_from(&SourcePath::default(), path)
32 }106 }
33 /// Resolves absolute path, doesn't supports jpath and other fancy things
34 fn resolve(&self, path: &Path) -> Result<SourcePath> {
35 bail!(AbsoluteImportNotSupported(path.to_owned()))
36 }
37107
38 /// Load resolved file108 /// Load resolved file
39 /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],109 /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],
105}175}
106176
107impl ImportResolver for FileImportResolver {177impl ImportResolver for FileImportResolver {
108 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {178 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {
179 let path = path.as_path();
109 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {180 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {
110 let mut o = f.path().to_owned();181 let mut o = f.path().to_owned();
111 o.pop();182 o.pop();
131 }202 }
132 bail!(ImportFileNotFound(from.clone(), path.to_owned()))203 bail!(ImportFileNotFound(from.clone(), path.to_owned()))
133 }204 }
134 fn resolve(&self, path: &Path) -> Result<SourcePath> {
135 let Some(source) = check_path(path)? else {
136 bail!(AbsoluteImportFileNotFound(path.to_owned()))
137 };
138 Ok(source)
139 }
140205
141 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {206 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {
142 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {207 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {
155 Ok(out)220 Ok(out)
156 }221 }
157222
158 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {223 fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {
159 self.resolve_from(&SourcePath::default(), path)224 self.resolve_from(&SourcePath::default(), path)
160 }225 }
161}226}
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
29 cell::{RefCell, RefMut},29 cell::{RefCell, RefMut},
30 collections::hash_map::Entry,30 collections::hash_map::Entry,
31 fmt::{self, Debug},31 fmt::{self, Debug},
32 path::Path,
33 rc::Rc,32 rc::Rc,
34};33};
3534
349 }348 }
350349
351 /// Has same semantics as `import 'path'` called from `from` file350 /// Has same semantics as `import 'path'` called from `from` file
352 pub fn import_from(&self, from: &SourcePath, path: &str) -> Result<Val> {351 pub fn import_from(&self, from: &SourcePath, path: impl AsPathLike) -> Result<Val> {
353 let resolved = self.resolve_from(from, path)?;352 let resolved = self.resolve_from(from, &path)?;
354 self.import_resolved(resolved)353 self.import_resolved(resolved)
355 }354 }
356 pub fn import(&self, path: impl AsRef<Path>) -> Result<Val> {355 pub fn import(&self, path: impl AsPathLike) -> Result<Val> {
357 let resolved = self.resolve(path)?;356 let resolved = self.resolve_from_default(&path)?;
358 self.import_resolved(resolved)357 self.import_resolved(resolved)
359 }358 }
360359
468impl State {467impl State {
469 // Only panics in case of [`ImportResolver`] contract violation468 // Only panics in case of [`ImportResolver`] contract violation
470 #[allow(clippy::missing_panics_doc)]469 #[allow(clippy::missing_panics_doc)]
471 pub fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {470 pub fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {
472 self.import_resolver().resolve_from(from, path.as_ref())471 self.import_resolver().resolve_from(from, path)
473 }472 }
474
475 // Only panics in case of [`ImportResolver`] contract violation
476 #[allow(clippy::missing_panics_doc)]473 #[allow(clippy::missing_panics_doc)]
477 pub fn resolve(&self, path: impl AsRef<Path>) -> Result<SourcePath> {474 pub fn resolve_from_default(&self, path: &dyn AsPathLike) -> Result<SourcePath> {
478 self.import_resolver().resolve(path.as_ref())475 self.import_resolver().resolve_from_default(path)
479 }476 }
480 pub fn import_resolver(&self) -> &dyn ImportResolver {477 pub fn import_resolver(&self) -> &dyn ImportResolver {
481 &*self.0.import_resolver478 &*self.0.import_resolver
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
12pub use encoding::*;12pub use encoding::*;
13pub use hash::*;13pub use hash::*;
14use jrsonnet_evaluator::{14use jrsonnet_evaluator::{
15 error::{ErrorKind::*, Result},15 error::Result,
16 function::{CallLocation, FuncVal, TlaArg},16 function::{CallLocation, FuncVal, TlaArg},
17 trace::PathResolver,17 trace::PathResolver,
18 val::NumValue,18 val::NumValue,
377 .ext_vars377 .ext_vars
378 .insert(name, TlaArg::String(value));378 .insert(name, TlaArg::String(value));
379 }379 }
380 pub fn add_ext_code(&self, name: &str, code: impl Into<IStr>) -> Result<()> {380 pub fn add_ext_code(&self, name: &str, code: impl AsRef<str>) -> Result<()> {
381 let code = code.into();
382 let source = extvar_source(name, code.clone());
383 let parsed = jrsonnet_parser::parse(
384 &code,
385 &jrsonnet_parser::ParserSettings {
386 source: source.clone(),
387 },
388 )
389 .map_err(|e| ImportSyntaxError {
390 path: source,
391 error: Box::new(e),
392 })?;
393 // self.data_mut().volatile_files.insert(source_name, code);381 // self.data_mut().volatile_files.insert(source_name, code);
394 self.settings_mut()382 self.settings_mut()
395 .ext_vars383 .ext_vars
396 .insert(name.into(), TlaArg::Code(parsed));384 .insert(name.into(), TlaArg::InlineCode(code.as_ref().to_owned()));
397 Ok(())385 Ok(())
398 }386 }
399 pub fn add_native(&self, name: impl Into<IStr>, cb: impl Into<FuncVal>) {387 pub fn add_native(&self, name: impl Into<IStr>, cb: impl Into<FuncVal>) {