git.delta.rocks / jrsonnet / refs/commits / b8bef138ffa9

difftreelog

Merge pull request #84 from CertainLach/split-stdlib

Yaroslav Bolyukin2022-10-17parents: #e831ab7 #c39582b.patch.diff
in: master
Separate jrsonnet-evaluator and stdlib implementation #82

173 files changed

deleted.envrcdiffbeforeafterboth

no changes

deleted.gitlab-ci.ymldiffbeforeafterboth

no changes

modifiedCargo.lockdiffbeforeafterboth
8787
88[[package]]88[[package]]
89name = "clap"89name = "clap"
90version = "3.1.18"90version = "3.2.8"
91source = "registry+https://github.com/rust-lang/crates.io-index"91source = "registry+https://github.com/rust-lang/crates.io-index"
92checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"92checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
93dependencies = [93dependencies = [
94 "atty",94 "atty",
95 "bitflags",95 "bitflags",
96 "clap_derive",96 "clap_derive",
97 "clap_lex",97 "clap_lex",
98 "indexmap",98 "indexmap",
99 "lazy_static",99 "once_cell",
100 "strsim",100 "strsim",
101 "termcolor",101 "termcolor",
102 "textwrap",102 "textwrap",
103]103]
104104
105[[package]]105[[package]]
106name = "clap_complete"106name = "clap_complete"
107version = "3.1.4"107version = "3.2.3"
108source = "registry+https://github.com/rust-lang/crates.io-index"108source = "registry+https://github.com/rust-lang/crates.io-index"
109checksum = "da92e6facd8d73c22745a5d3cbb59bdf8e46e3235c923e516527d8e81eec14a4"109checksum = "ead064480dfc4880a10764488415a97fdd36a4cf1bb022d372f02e8faf8386e1"
110dependencies = [110dependencies = [
111 "clap",111 "clap",
112]112]
113113
114[[package]]114[[package]]
115name = "clap_derive"115name = "clap_derive"
116version = "3.1.18"116version = "3.2.7"
117source = "registry+https://github.com/rust-lang/crates.io-index"117source = "registry+https://github.com/rust-lang/crates.io-index"
118checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c"118checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902"
119dependencies = [119dependencies = [
120 "heck",120 "heck",
121 "proc-macro-error",121 "proc-macro-error",
126126
127[[package]]127[[package]]
128name = "clap_lex"128name = "clap_lex"
129version = "0.2.0"129version = "0.2.4"
130source = "registry+https://github.com/rust-lang/crates.io-index"130source = "registry+https://github.com/rust-lang/crates.io-index"
131checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"131checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
132dependencies = [132dependencies = [
133 "os_str_bytes",133 "os_str_bytes",
134]134]
221 "jrsonnet-evaluator",221 "jrsonnet-evaluator",
222 "jrsonnet-gcmodule",222 "jrsonnet-gcmodule",
223 "jrsonnet-parser",223 "jrsonnet-parser",
224 "jrsonnet-stdlib",
224]225]
225226
226[[package]]227[[package]]
229dependencies = [230dependencies = [
230 "annotate-snippets",231 "annotate-snippets",
231 "anyhow",232 "anyhow",
232 "base64",
233 "bincode",233 "bincode",
234 "hashbrown 0.12.1",234 "hashbrown 0.12.1",
235 "jrsonnet-gcmodule",235 "jrsonnet-gcmodule",
236 "jrsonnet-interner",236 "jrsonnet-interner",
237 "jrsonnet-macros",237 "jrsonnet-macros",
238 "jrsonnet-parser",238 "jrsonnet-parser",
239 "jrsonnet-stdlib",
240 "jrsonnet-types",239 "jrsonnet-types",
241 "md5",
242 "pathdiff",240 "pathdiff",
243 "rustc-hash",241 "rustc-hash",
244 "serde",242 "serde",
245 "serde_json",243 "serde_json",
246 "serde_yaml_with_quirks",
247 "static_assertions",244 "static_assertions",
248 "strsim",245 "strsim",
249 "thiserror",246 "thiserror",
278 "jrsonnet-gcmodule",275 "jrsonnet-gcmodule",
279 "rustc-hash",276 "rustc-hash",
280 "serde",277 "serde",
278 "structdump",
281]279]
282280
283[[package]]281[[package]]
295dependencies = [293dependencies = [
296 "jrsonnet-gcmodule",294 "jrsonnet-gcmodule",
297 "jrsonnet-interner",295 "jrsonnet-interner",
298 "jrsonnet-stdlib",
299 "peg",296 "peg",
300 "serde",297 "serde",
301 "static_assertions",298 "static_assertions",
299 "structdump",
302]300]
303301
304[[package]]302[[package]]
305name = "jrsonnet-stdlib"303name = "jrsonnet-stdlib"
306version = "0.4.2"304version = "0.4.2"
305dependencies = [
306 "base64",
307 "bincode",
308 "jrsonnet-evaluator",
309 "jrsonnet-gcmodule",
310 "jrsonnet-macros",
311 "jrsonnet-parser",
312 "md5",
313 "serde",
314 "serde_json",
315 "serde_yaml_with_quirks",
316 "structdump",
317]
307318
308[[package]]319[[package]]
309name = "jrsonnet-types"320name = "jrsonnet-types"
313 "peg",324 "peg",
314]325]
315
316[[package]]
317name = "jsonnet"
318version = "0.4.2"
319dependencies = [
320 "jrsonnet-evaluator",
321 "jrsonnet-gcmodule",
322 "jrsonnet-parser",
323]
324
325[[package]]
326name = "lazy_static"
327version = "1.4.0"
328source = "registry+https://github.com/rust-lang/crates.io-index"
329checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
330326
331[[package]]327[[package]]
332name = "libc"328name = "libc"
333version = "0.2.126"329version = "0.2.126"
334source = "registry+https://github.com/rust-lang/crates.io-index"330source = "registry+https://github.com/rust-lang/crates.io-index"
335checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"331checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
332
333[[package]]
334name = "libjsonnet"
335version = "0.4.2"
336dependencies = [
337 "jrsonnet-evaluator",
338 "jrsonnet-gcmodule",
339 "jrsonnet-parser",
340 "jrsonnet-stdlib",
341]
336342
337[[package]]343[[package]]
338name = "linked-hash-map"344name = "linked-hash-map"
339version = "0.5.4"345version = "0.5.6"
340source = "registry+https://github.com/rust-lang/crates.io-index"346source = "registry+https://github.com/rust-lang/crates.io-index"
341checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"347checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
342348
343[[package]]349[[package]]
344name = "lock_api"350name = "lock_api"
511517
512[[package]]518[[package]]
513name = "serde"519name = "serde"
514version = "1.0.137"520version = "1.0.142"
515source = "registry+https://github.com/rust-lang/crates.io-index"521source = "registry+https://github.com/rust-lang/crates.io-index"
516checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"522checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2"
517dependencies = [523dependencies = [
518 "serde_derive",524 "serde_derive",
519]525]
520526
521[[package]]527[[package]]
522name = "serde_derive"528name = "serde_derive"
523version = "1.0.137"529version = "1.0.142"
524source = "registry+https://github.com/rust-lang/crates.io-index"530source = "registry+https://github.com/rust-lang/crates.io-index"
525checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"531checksum = "34b5b8d809babe02f538c2cfec6f2c1ed10804c0e5a6a041a049a4f5588ccc2e"
526dependencies = [532dependencies = [
527 "proc-macro2",533 "proc-macro2",
528 "quote",534 "quote",
531537
532[[package]]538[[package]]
533name = "serde_json"539name = "serde_json"
534version = "1.0.81"540version = "1.0.82"
535source = "registry+https://github.com/rust-lang/crates.io-index"541source = "registry+https://github.com/rust-lang/crates.io-index"
536checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"542checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
537dependencies = [543dependencies = [
538 "indexmap",544 "indexmap",
539 "itoa",545 "itoa",
571source = "registry+https://github.com/rust-lang/crates.io-index"577source = "registry+https://github.com/rust-lang/crates.io-index"
572checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"578checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
579
580[[package]]
581name = "structdump"
582version = "0.2.0"
583source = "registry+https://github.com/rust-lang/crates.io-index"
584checksum = "b0570327507bf281d8a6e6b0d4c082b12cb6bcee27efce755aa5efacd44076c1"
585dependencies = [
586 "proc-macro2",
587 "quote",
588 "structdump-derive",
589]
590
591[[package]]
592name = "structdump-derive"
593version = "0.2.0"
594source = "registry+https://github.com/rust-lang/crates.io-index"
595checksum = "29cc0b59cfa11f1bceda09a9a7e37e6a6c3138575fd24ade8aa9af6d09aedf28"
596dependencies = [
597 "proc-macro2",
598 "quote",
599 "syn",
600]
573601
574[[package]]602[[package]]
575name = "syn"603name = "syn"
591 "winapi-util",619 "winapi-util",
592]620]
621
622[[package]]
623name = "tests"
624version = "0.1.0"
625dependencies = [
626 "jrsonnet-evaluator",
627 "jrsonnet-gcmodule",
628 "jrsonnet-stdlib",
629 "serde",
630]
593631
594[[package]]632[[package]]
595name = "textwrap"633name = "textwrap"
modifiedCargo.tomldiffbeforeafterboth
1[workspace]1[workspace]
2members = ["crates/*", "bindings/jsonnet", "cmds/jrsonnet"]2members = ["crates/*", "bindings/jsonnet", "cmds/jrsonnet", "tests"]
33
4[profile.test]4[profile.test]
5opt-level = 15opt-level = 1
modifiedbindings/jsonnet/Cargo.tomldiffbeforeafterboth
1[package]1[package]
2name = "jsonnet"2name = "libjsonnet"
3description = "Rust implementation of libjsonnet.so"3description = "Rust implementation of libjsonnet.so"
4version = "0.4.2"4version = "0.4.2"
5authors = ["Yaroslav Bolyukin <iam@lach.pw>"]5authors = ["Yaroslav Bolyukin <iam@lach.pw>"]
10[dependencies]10[dependencies]
11jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2" }11jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2" }
12jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }12jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }
13jrsonnet-stdlib = { path = "../../crates/jrsonnet-stdlib", version = "0.4.2" }
13jrsonnet-gcmodule = { version = "0.3.4" }14jrsonnet-gcmodule = { version = "0.3.4" }
1415
15[lib]16[lib]
17name = "jsonnet"
16crate-type = ["cdylib"]18crate-type = ["cdylib"]
1719
18[features]20[features]
21# Export additional functions for native integration, i.e ability to set custom trace format
19interop = []22interop = []
20experimental = ["exp-preserve-order", "exp-destruct"]23experimental = ["exp-preserve-order", "exp-destruct"]
21exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]24exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]
modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
4 any::Any,4 any::Any,
5 cell::RefCell,5 cell::RefCell,
6 collections::HashMap,6 collections::HashMap,
7 env::current_dir,
7 ffi::{c_void, CStr, CString},8 ffi::{c_void, CStr, CString},
8 fs::File,
9 io::Read,
10 os::raw::{c_char, c_int},9 os::raw::{c_char, c_int},
11 path::{Path, PathBuf},10 path::PathBuf,
12 ptr::null_mut,11 ptr::null_mut,
13};12};
1413
15use jrsonnet_evaluator::{14use jrsonnet_evaluator::{
16 error::{Error::*, Result},15 error::{Error::*, Result},
17 throw, ImportResolver, State,16 throw, FileImportResolver, ImportResolver, State,
18};17};
18use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};
1919
20pub type JsonnetImportCallback = unsafe extern "C" fn(20pub type JsonnetImportCallback = unsafe extern "C" fn(
21 ctx: *mut c_void,21 ctx: *mut c_void,
29pub struct CallbackImportResolver {29pub struct CallbackImportResolver {
30 cb: JsonnetImportCallback,30 cb: JsonnetImportCallback,
31 ctx: *mut c_void,31 ctx: *mut c_void,
32 out: RefCell<HashMap<PathBuf, Vec<u8>>>,32 out: RefCell<HashMap<SourcePath, Vec<u8>>>,
33}33}
34impl ImportResolver for CallbackImportResolver {34impl ImportResolver for CallbackImportResolver {
35 fn resolve_file(&self, from: &Path, path: &str) -> Result<PathBuf> {35 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
36 let base = CString::new(from.to_str().unwrap()).unwrap().into_raw();36 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {
37 let mut o = p.path().to_owned();
38 o.pop();
39 o
40 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {
41 d.path().to_owned()
42 } else if from.is_default() {
43 current_dir().map_err(|e| ImportIo(e.to_string()))?
44 } else {
45 unreachable!("can't resolve this path");
46 };
47 let base = unsafe { crate::unparse_path(&base) };
37 let rel = CString::new(path).unwrap().into_raw();48 let rel = CString::new(path).unwrap();
38 let found_here: *mut c_char = null_mut();49 let found_here: *mut c_char = null_mut();
39 let mut success: i32 = 0;50 let mut success: i32 = 0;
40 let result_ptr = unsafe {51 let result_ptr = unsafe {
41 (self.cb)(52 (self.cb)(
42 self.ctx,53 self.ctx,
43 base,54 base.as_ptr(),
44 rel,55 rel.as_ptr(),
45 &mut (found_here as *const _),56 &mut (found_here as *const _),
46 &mut success,57 &mut success,
47 )58 )
48 };59 };
49 // Release memory occipied by arguments passed
50 unsafe {
51 let _ = CString::from_raw(base);
52 let _ = CString::from_raw(rel);
53 }
54 let result_raw = unsafe { CStr::from_ptr(result_ptr) };60 let result_raw = unsafe { CStr::from_ptr(result_ptr) };
55 let result_str = result_raw.to_str().unwrap();61 let result_str = result_raw.to_str().unwrap();
56 assert!(success == 0 || success == 1);62 assert!(success == 0 || success == 1);
61 }67 }
6268
63 let found_here_raw = unsafe { CStr::from_ptr(found_here) };69 let found_here_raw = unsafe { CStr::from_ptr(found_here) };
64 let found_here_buf = PathBuf::from(found_here_raw.to_str().unwrap());70 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(
71 found_here_raw.to_str().unwrap(),
72 )));
65 unsafe {73 unsafe {
66 let _ = CString::from_raw(found_here);74 let _ = CString::from_raw(found_here);
67 }75 }
7482
75 Ok(found_here_buf)83 Ok(found_here_buf)
76 }84 }
77 fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>> {85 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {
78 Ok(self.out.borrow().get(resolved).unwrap().clone())86 Ok(self.out.borrow().get(resolved).unwrap().clone())
79 }87 }
8088
81 unsafe fn as_any(&self) -> &dyn Any {89 fn as_any(&self) -> &dyn Any {
82 self90 self
83 }91 }
84}92}
8593
86/// # Safety94/// # Safety
95///
96/// It should be safe to call `cb` using valid values with passed `ctx`
87#[no_mangle]97#[no_mangle]
88pub unsafe extern "C" fn jsonnet_import_callback(98pub unsafe extern "C" fn jsonnet_import_callback(
89 vm: &State,99 vm: &State,
97 }))107 }))
98}108}
99
100/// Standard FS import resolver
101#[derive(Default)]
102pub struct NativeImportResolver {
103 library_paths: RefCell<Vec<PathBuf>>,
104}
105impl NativeImportResolver {
106 fn add_jpath(&self, path: PathBuf) {
107 self.library_paths.borrow_mut().push(path);
108 }
109}
110impl ImportResolver for NativeImportResolver {
111 fn resolve_file(&self, from: &Path, path: &str) -> Result<PathBuf> {
112 let mut new_path = from.to_owned();
113 new_path.push(path);
114 if new_path.exists() {
115 Ok(new_path)
116 } else {
117 for library_path in self.library_paths.borrow().iter() {
118 let mut cloned = library_path.clone();
119 cloned.push(path);
120 if cloned.exists() {
121 return Ok(cloned);
122 }
123 }
124 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))
125 }
126 }
127 fn load_file_contents(&self, id: &Path) -> Result<Vec<u8>> {
128 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;
129 let mut out = Vec::new();
130 file.read_to_end(&mut out)
131 .map_err(|e| ImportIo(e.to_string()))?;
132 Ok(out)
133 }
134 unsafe fn as_any(&self) -> &dyn Any {
135 self
136 }
137}
138109
139/// # Safety110/// # Safety
140///111///
141/// This function is safe, if received v is a pointer to normal C string112/// `path` should be a NUL-terminated string
142#[no_mangle]113#[no_mangle]
143pub unsafe extern "C" fn jsonnet_jpath_add(vm: &State, v: *const c_char) {114pub unsafe extern "C" fn jsonnet_jpath_add(vm: &State, path: *const c_char) {
144 let cstr = CStr::from_ptr(v);115 let cstr = CStr::from_ptr(path);
145 let path = PathBuf::from(cstr.to_str().unwrap());116 let path = PathBuf::from(cstr.to_str().unwrap());
146 let any_resolver = &vm.settings().import_resolver;117 let any_resolver = vm.import_resolver();
147 let resolver = any_resolver118 let resolver = any_resolver
148 .as_any()119 .as_any()
149 .downcast_ref::<NativeImportResolver>()120 .downcast_ref::<FileImportResolver>()
150 .expect("jpaths are not compatible with callback imports!");121 .expect("jpaths are not compatible with callback imports!");
151 resolver.add_jpath(path);122 resolver.add_jpath(path);
152}123}
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
1010
11use std::{11use std::{
12 alloc::Layout,12 alloc::Layout,
13 borrow::Cow,
13 ffi::{CStr, CString},14 ffi::{CStr, CString, OsStr},
14 os::raw::{c_char, c_double, c_int, c_uint},15 os::raw::{c_char, c_double, c_int, c_uint},
15 path::PathBuf,16 path::Path,
16};17};
1718
18use import::NativeImportResolver;
19use jrsonnet_evaluator::{IStr, ManifestFormat, State, Val};19use jrsonnet_evaluator::{
20 trace::PathResolver, FileImportResolver, IStr, ManifestFormat, State, Val,
21};
2022
21/// WASM stub23/// WASM stub
22#[cfg(target_arch = "wasm32")]24#[cfg(target_arch = "wasm32")]
23#[no_mangle]25#[no_mangle]
24pub extern "C" fn _start() {}26pub extern "C" fn _start() {}
2527
28/// Return the version string of the Jsonnet interpreter.
29/// Conforms to [semantic versioning](http://semver.org/).
30/// If this does not match `LIB_JSONNET_VERSION`
31/// then there is a mismatch between header and compiled library.
26#[no_mangle]32#[no_mangle]
27pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {33pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {
28 b"v0.16.0\0"34 b"v0.16.0\0"
29}35}
3036
37unsafe fn parse_path(input: &CStr) -> Cow<Path> {
38 #[cfg(target_family = "unix")]
39 {
40 use std::os::unix::ffi::OsStrExt;
41 let str = OsStr::from_bytes(input.to_bytes());
42 Cow::Borrowed(Path::new(str))
43 }
44 #[cfg(not(target_family = "unix"))]
45 {
46 let string = input.to_str().expect("bad utf-8");
47 Cow::Borrowed(string.as_ref())
48 }
49}
50
51unsafe fn unparse_path(input: &Path) -> Cow<CStr> {
52 #[cfg(target_family = "unix")]
53 {
54 use std::os::unix::ffi::OsStrExt;
55 let str = CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it");
56 Cow::Owned(str)
57 }
58 #[cfg(not(target_family = "unix"))]
59 {
60 let str = input.as_os_str().to_str().expect("bad utf-8");
61 let cstr = CString::new(str).expect("input has NUL inside");
62 Cow::Owned(cstr)
63 }
64}
65
66/// Creates a new Jsonnet virtual machine.
31#[no_mangle]67#[no_mangle]
68#[allow(clippy::box_default)]
32pub extern "C" fn jsonnet_make() -> *mut State {69pub extern "C" fn jsonnet_make() -> *mut State {
33 let state = State::default();70 let state = State::default();
34 state.with_stdlib();71 state.settings_mut().import_resolver = Box::new(FileImportResolver::default());
35 state.settings_mut().import_resolver = Box::new(NativeImportResolver::default());72 state.settings_mut().context_initializer = Box::new(jrsonnet_stdlib::ContextInitializer::new(
73 state.clone(),
74 PathResolver::new_cwd_fallback(),
75 ));
36 Box::into_raw(Box::new(state))76 Box::into_raw(Box::new(state))
37}77}
3878
39/// # Safety79/// Complement of [`jsonnet_vm_make`].
40#[no_mangle]80#[no_mangle]
41#[allow(clippy::boxed_local)]81#[allow(clippy::boxed_local)]
42pub unsafe extern "C" fn jsonnet_destroy(vm: *mut State) {82pub extern "C" fn jsonnet_destroy(vm: Box<State>) {
43 Box::from_raw(vm);83 drop(vm);
44}84}
4585
86/// Set the maximum stack depth.
46#[no_mangle]87#[no_mangle]
47pub extern "C" fn jsonnet_max_stack(vm: &State, v: c_uint) {88pub extern "C" fn jsonnet_max_stack(vm: &State, v: c_uint) {
48 vm.settings_mut().max_stack = v as usize;89 vm.settings_mut().max_stack = v as usize;
49}90}
5091
51// jrsonnet currently have no GC, so these functions is no-op92/// Set the number of objects required before a garbage collection cycle is allowed.
93///
94/// No-op for now
52#[no_mangle]95#[no_mangle]
53pub extern "C" fn jsonnet_gc_min_objects(_vm: &State, _v: c_uint) {}96pub extern "C" fn jsonnet_gc_min_objects(_vm: &State, _v: c_uint) {}
97
98/// Run the garbage collector after this amount of growth in the number of objects
99///
100/// No-op for now
54#[no_mangle]101#[no_mangle]
55pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &State, _v: c_double) {}102pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &State, _v: c_double) {}
56103
104/// Expect a string as output and don't JSON encode it.
57#[no_mangle]105#[no_mangle]
58pub extern "C" fn jsonnet_string_output(vm: &State, v: c_int) {106pub extern "C" fn jsonnet_string_output(vm: &State, v: c_int) {
59 match v {107 match v {
67 }115 }
68}116}
69117
118/// Allocate, resize, or free a buffer. This will abort if the memory cannot be allocated. It will
119/// only return NULL if sz was zero.
120///
70/// # Safety121/// # Safety
122///
123/// `buf` should be either previosly allocated by this library, or NULL
71///124///
72/// This function is most definitely broken, but it works somehow, see TODO inside125/// This function is most definitely broken, but it works somehow, see TODO inside
73#[no_mangle]126#[no_mangle]
74pub unsafe extern "C" fn jsonnet_realloc(_vm: &State, buf: *mut u8, sz: usize) -> *mut u8 {127pub unsafe extern "C" fn jsonnet_realloc(_vm: &State, buf: *mut u8, sz: usize) -> *mut u8 {
75 if buf.is_null() {128 if buf.is_null() {
76 assert!(sz != 0);129 if sz == 0 {
130 return std::ptr::null_mut();
131 }
77 return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());132 return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());
78 }133 }
79 // TODO: Somehow store size of allocation, because its real size is probally not 16 :D134 // TODO: Somehow store size of allocation, because its real size is probally not 16 :D
88 std::alloc::realloc(buf, old_layout, sz)143 std::alloc::realloc(buf, old_layout, sz)
89}144}
90145
146/// Clean up a JSON subtree.
91/// # Safety147///
148/// This is useful if you want to abort with an error mid-way through building a complex value.
92#[no_mangle]149#[no_mangle]
93#[allow(clippy::boxed_local)]150#[allow(clippy::boxed_local)]
94pub unsafe extern "C" fn jsonnet_json_destroy(_vm: &State, v: *mut Val) {151pub extern "C" fn jsonnet_json_destroy(_vm: &State, v: Box<Val>) {
95 Box::from_raw(v);152 drop(v);
96}153}
97154
155/// Set the number of lines of stack trace to display (0 for all of them).
98#[no_mangle]156#[no_mangle]
99pub extern "C" fn jsonnet_max_trace(vm: &State, v: c_uint) {157pub extern "C" fn jsonnet_max_trace(vm: &State, v: c_uint) {
100 vm.set_max_trace(v as usize)158 vm.set_max_trace(v as usize)
101}159}
102160
161/// Evaluate a file containing Jsonnet code, return a JSON string.
162///
163/// The returned string should be cleaned up with jsonnet_realloc.
164///
103/// # Safety165/// # Safety
104///166///
105/// This function is safe, if received v is a pointer to normal C string167/// `filename` should be a NUL-terminated string
106#[no_mangle]168#[no_mangle]
107pub unsafe extern "C" fn jsonnet_evaluate_file(169pub unsafe extern "C" fn jsonnet_evaluate_file(
108 vm: &State,170 vm: &State,
109 filename: *const c_char,171 filename: *const c_char,
110 error: &mut c_int,172 error: &mut c_int,
111) -> *const c_char {173) -> *const c_char {
112 let filename = CStr::from_ptr(filename);174 let filename = parse_path(CStr::from_ptr(filename));
113 match vm175 match vm
114 .import(PathBuf::from(filename.to_str().unwrap()))176 .import(&filename)
115 .and_then(|v| vm.with_tla(v))177 .and_then(|v| vm.with_tla(v))
116 .and_then(|v| vm.manifest(v))178 .and_then(|v| vm.manifest(v))
117 {179 {
127 }189 }
128}190}
129191
192/// Evaluate a string containing Jsonnet code, return a JSON string.
193///
194/// The returned string should be cleaned up with jsonnet_realloc.
195///
130/// # Safety196/// # Safety
131///197///
132/// This function is safe, if received v is a pointer to normal C string198/// `filename`, `snippet` should be a NUL-terminated strings
133#[no_mangle]199#[no_mangle]
134pub unsafe extern "C" fn jsonnet_evaluate_snippet(200pub unsafe extern "C" fn jsonnet_evaluate_snippet(
135 vm: &State,201 vm: &State,
141 let snippet = CStr::from_ptr(snippet);207 let snippet = CStr::from_ptr(snippet);
142 match vm208 match vm
143 .evaluate_snippet(209 .evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
144 filename.to_str().unwrap().into(),
145 snippet.to_str().unwrap().into(),
146 )
147 .and_then(|v| vm.with_tla(v))210 .and_then(|v| vm.with_tla(v))
148 .and_then(|v| vm.manifest(v))211 .and_then(|v| vm.manifest(v))
183 filename: *const c_char,246 filename: *const c_char,
184 error: &mut c_int,247 error: &mut c_int,
185) -> *const c_char {248) -> *const c_char {
186 let filename = CStr::from_ptr(filename);249 let filename = parse_path(CStr::from_ptr(filename));
187 match vm250 match vm
188 .import(PathBuf::from(filename.to_str().unwrap()))251 .import(&filename)
189 .and_then(|v| vm.with_tla(v))252 .and_then(|v| vm.with_tla(v))
190 .and_then(|v| vm.manifest_multi(v))253 .and_then(|v| vm.manifest_multi(v))
191 {254 {
213 let snippet = CStr::from_ptr(snippet);276 let snippet = CStr::from_ptr(snippet);
214 match vm277 match vm
215 .evaluate_snippet(278 .evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
216 filename.to_str().unwrap().into(),
217 snippet.to_str().unwrap().into(),
218 )
219 .and_then(|v| vm.with_tla(v))279 .and_then(|v| vm.with_tla(v))
220 .and_then(|v| vm.manifest_multi(v))280 .and_then(|v| vm.manifest_multi(v))
253 filename: *const c_char,313 filename: *const c_char,
254 error: &mut c_int,314 error: &mut c_int,
255) -> *const c_char {315) -> *const c_char {
256 let filename = CStr::from_ptr(filename);316 let filename = parse_path(CStr::from_ptr(filename));
257 match vm317 match vm
258 .import(PathBuf::from(filename.to_str().unwrap()))318 .import(&filename)
259 .and_then(|v| vm.with_tla(v))319 .and_then(|v| vm.with_tla(v))
260 .and_then(|v| vm.manifest_stream(v))320 .and_then(|v| vm.manifest_stream(v))
261 {321 {
266 Err(e) => {326 Err(e) => {
267 *error = 1;327 *error = 1;
268 let out = vm.stringify_err(&e);328 let out = vm.stringify_err(&e);
269 CString::new(&out as &str).unwrap().into_raw()329 CString::new(&out as &str)
330 .expect("there should be no \\0 in the error string")
331 .into_raw()
270 }332 }
271 }333 }
283 let snippet = CStr::from_ptr(snippet);345 let snippet = CStr::from_ptr(snippet);
284 match vm346 match vm
285 .evaluate_snippet(347 .evaluate_snippet(
286 filename.to_str().unwrap().into(),348 filename.to_str().expect("filename is not utf-8"),
287 snippet.to_str().unwrap().into(),349 snippet.to_str().expect("snippet is not utf-8"),
288 )350 )
289 .and_then(|v| vm.with_tla(v))351 .and_then(|v| vm.with_tla(v))
290 .and_then(|v| vm.manifest_stream(v))352 .and_then(|v| vm.manifest_stream(v))
296 Err(e) => {358 Err(e) => {
297 *error = 1;359 *error = 1;
298 let out = vm.stringify_err(&e);360 let out = vm.stringify_err(&e);
299 CString::new(&out as &str).unwrap().into_raw()361 CString::new(&out as &str)
362 .expect("there should be no \\0 in the error string")
363 .into_raw()
300 }364 }
301 }365 }
modifiedbindings/jsonnet/src/native.rsdiffbeforeafterboth
1use std::{1use std::{
2 borrow::Cow,
2 ffi::{c_void, CStr},3 ffi::{c_void, CStr},
3 os::raw::{c_char, c_int},4 os::raw::{c_char, c_int},
4};5};
56
6use jrsonnet_evaluator::{7use jrsonnet_evaluator::{
7 error::{Error, LocError},8 error::{Error, LocError},
8 function::builtin::{BuiltinParam, NativeCallback, NativeCallbackHandler},9 function::builtin::{NativeCallback, NativeCallbackHandler},
9 tb,10 tb,
10 typed::Typed,11 typed::Typed,
11 IStr, State, Val,12 IStr, State, Val,
12};13};
13use jrsonnet_gcmodule::Cc;14use jrsonnet_gcmodule::Cc;
1415
16/// The returned `JsonnetJsonValue*` should be allocated with `jsonnet_realloc`. It will be cleaned up
17/// along with the objects rooted at `argv` by `libjsonnet` when no-longer needed. Return a string upon
18/// failure, which will appear in Jsonnet as an error. The `argv` pointer is an array whose size
19/// matches the array of parameters supplied when the native callback was originally registered.
20///
21/// - `ctx` User pointer, given in jsonnet_native_callback.
22/// - `argv` Array of arguments from Jsonnet code.
23/// - `param` success Set this byref param to 1 to indicate success and 0 for failure.
24/// Returns the content of the imported file, or an error message.
15type JsonnetNativeCallback = unsafe extern "C" fn(25type JsonnetNativeCallback = unsafe extern "C" fn(
16 ctx: *const c_void,26 ctx: *const c_void,
17 argv: *const *const Val,27 argv: *const *const Val,
44 if success == 1 {54 if success == 1 {
45 Ok(v)55 Ok(v)
46 } else {56 } else {
47 let e = IStr::from_untyped(v, s).expect("error msg");57 let e = IStr::from_untyped(v, s).expect("error msg should be a string");
48 Err(Error::RuntimeError(e).into())58 Err(Error::RuntimeError(e).into())
49 }59 }
50 }60 }
51}61}
5262
63/// Callback to provide native extensions to Jsonnet.
64///
53/// # Safety65/// # Safety
66///
67/// `vm` should be a vm allocated by `jsonnet_make`
68/// `name` should be a NUL-terminated string
69/// `cb` should be a function pointer
70/// `raw_params` should point to a NULL-terminated array of NUL-terminated strings
54#[no_mangle]71#[no_mangle]
55pub unsafe extern "C" fn jsonnet_native_callback(72pub unsafe extern "C" fn jsonnet_native_callback(
56 vm: &State,73 vm: &State,
59 ctx: *const c_void,76 ctx: *const c_void,
60 mut raw_params: *const *const c_char,77 mut raw_params: *const *const c_char,
61) {78) {
62 let name = CStr::from_ptr(name).to_str().expect("utf8 name").into();79 let name = CStr::from_ptr(name)
80 .to_str()
81 .expect("name is not utf-8")
82 .into();
63 let mut params = Vec::new();83 let mut params = Vec::new();
64 loop {84 loop {
65 if (*raw_params).is_null() {85 if (*raw_params).is_null() {
66 break;86 break;
67 }87 }
68 let param = CStr::from_ptr(*raw_params).to_str().expect("not utf8");88 let param = CStr::from_ptr(*raw_params)
89 .to_str()
90 .expect("param name is not utf-8");
69 params.push(BuiltinParam {91 params.push(Cow::Owned(param.into()));
70 name: param.into(),
71 has_default: false,
72 });
73 raw_params = raw_params.offset(1);92 raw_params = raw_params.offset(1);
74 }93 }
7594
95 let any_resolver = vm.context_initializer();
96 any_resolver
97 .as_any()
98 .downcast_ref::<jrsonnet_stdlib::ContextInitializer>()
99 .expect("only stdlib context initializer supported")
76 vm.add_native(100 .add_native(
77 name,101 name,
78 #[allow(deprecated)]102 #[allow(deprecated)]
79 Cc::new(tb!(NativeCallback::new(103 Cc::new(tb!(NativeCallback::new(
modifiedbindings/jsonnet/src/val_extract.rsdiffbeforeafterboth
77
8use jrsonnet_evaluator::{State, Val};8use jrsonnet_evaluator::{State, Val};
99
10/// If the value is a string, return it as UTF-8, otherwise return `NULL`.
10#[no_mangle]11#[no_mangle]
11pub extern "C" fn jsonnet_json_extract_string(_vm: &State, v: &Val) -> *mut c_char {12pub extern "C" fn jsonnet_json_extract_string(_vm: &State, v: &Val) -> *mut c_char {
12 match v {13 match v {
13 Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),14 Val::Str(s) => CString::new(s as &str).unwrap().into_raw(),
14 _ => std::ptr::null_mut(),15 _ => std::ptr::null_mut(),
15 }16 }
16}17}
18
19/// If the value is a number, return `1` and store the number in out, otherwise return `0`.
17#[no_mangle]20#[no_mangle]
18pub extern "C" fn jsonnet_json_extract_number(_vm: &State, v: &Val, out: &mut c_double) -> c_int {21pub extern "C" fn jsonnet_json_extract_number(_vm: &State, v: &Val, out: &mut c_double) -> c_int {
19 match v {22 match v {
25 }28 }
26}29}
30
31/// Return `0` if the value is `false`, `1` if it is `true`, and `2` if it is not a `bool`.
27#[no_mangle]32#[no_mangle]
28pub extern "C" fn jsonnet_json_extract_bool(_vm: &State, v: &Val) -> c_int {33pub extern "C" fn jsonnet_json_extract_bool(_vm: &State, v: &Val) -> c_int {
29 match v {34 match v {
33 }38 }
34}39}
40
41/// Return `1` if the value is `null`, otherwise return `0`.
35#[no_mangle]42#[no_mangle]
36pub extern "C" fn jsonnet_json_extract_null(_vm: &State, v: &Val) -> c_int {43pub extern "C" fn jsonnet_json_extract_null(_vm: &State, v: &Val) -> c_int {
37 match v {44 match v {
modifiedbindings/jsonnet/src/val_make.rsdiffbeforeafterboth
8use jrsonnet_evaluator::{val::ArrValue, ObjValue, State, Val};8use jrsonnet_evaluator::{val::ArrValue, ObjValue, State, Val};
9use jrsonnet_gcmodule::Cc;9use jrsonnet_gcmodule::Cc;
1010
11/// Convert the given `UTF-8` string to a `JsonnetJsonValue`.
12///
11/// # Safety13/// # Safety
12///14///
13/// This function is safe, if received v is a pointer to normal C string15/// `v` should be a NUL-terminated string
14#[no_mangle]16#[no_mangle]
15pub unsafe extern "C" fn jsonnet_json_make_string(_vm: &State, v: *const c_char) -> *mut Val {17pub unsafe extern "C" fn jsonnet_json_make_string(_vm: &State, val: *const c_char) -> *mut Val {
16 let cstr = CStr::from_ptr(v);18 let val = CStr::from_ptr(val);
17 let str = cstr.to_str().unwrap();19 let val = val.to_str().expect("string is not utf-8");
18 Box::into_raw(Box::new(Val::Str(str.into())))20 Box::into_raw(Box::new(Val::Str(val.into())))
19}21}
2022
23/// Convert the given double to a `JsonnetJsonValue`.
21#[no_mangle]24#[no_mangle]
22pub extern "C" fn jsonnet_json_make_number(_vm: &State, v: c_double) -> *mut Val {25pub extern "C" fn jsonnet_json_make_number(_vm: &State, v: c_double) -> *mut Val {
23 Box::into_raw(Box::new(Val::Num(v)))26 Box::into_raw(Box::new(Val::Num(v)))
24}27}
2528
29/// Convert the given `bool` (`1` or `0`) to a `JsonnetJsonValue`.
26#[no_mangle]30#[no_mangle]
27pub extern "C" fn jsonnet_json_make_bool(_vm: &State, v: c_int) -> *mut Val {31pub extern "C" fn jsonnet_json_make_bool(_vm: &State, v: c_int) -> *mut Val {
28 assert!(v == 0 || v == 1);32 assert!(v == 0 || v == 1, "bad boolean value");
29 Box::into_raw(Box::new(Val::Bool(v == 1)))33 Box::into_raw(Box::new(Val::Bool(v == 1)))
30}34}
3135
36/// Make a `JsonnetJsonValue` representing `null`.
32#[no_mangle]37#[no_mangle]
33pub extern "C" fn jsonnet_json_make_null(_vm: &State) -> *mut Val {38pub extern "C" fn jsonnet_json_make_null(_vm: &State) -> *mut Val {
34 Box::into_raw(Box::new(Val::Null))39 Box::into_raw(Box::new(Val::Null))
35}40}
3641
42/// Make a `JsonnetJsonValue` representing an array.
43///
44/// Assign elements with [`jsonnet_json_array_append`].
37#[no_mangle]45#[no_mangle]
38pub extern "C" fn jsonnet_json_make_array(_vm: &State) -> *mut Val {46pub extern "C" fn jsonnet_json_make_array(_vm: &State) -> *mut Val {
39 Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Cc::new(Vec::new())))))47 Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Cc::new(Vec::new())))))
40}48}
4149
50/// Make a `JsonnetJsonValue` representing an object.
42#[no_mangle]51#[no_mangle]
43pub extern "C" fn jsonnet_json_make_object(_vm: &State) -> *mut Val {52pub extern "C" fn jsonnet_json_make_object(_vm: &State) -> *mut Val {
44 Box::into_raw(Box::new(Val::Obj(ObjValue::new_empty())))53 Box::into_raw(Box::new(Val::Obj(ObjValue::new_empty())))
modifiedbindings/jsonnet/src/val_modify.rsdiffbeforeafterboth
7use jrsonnet_evaluator::{val::ArrValue, State, Thunk, Val};7use jrsonnet_evaluator::{val::ArrValue, State, Thunk, Val};
8use jrsonnet_gcmodule::Cc;8use jrsonnet_gcmodule::Cc;
99
10/// Adds value to the end of the array `arr`.
11///
10/// # Safety12/// # Safety
11///13///
12/// Received arr value should be correct pointer to array allocated by make_array14/// `arr` should be a pointer to array value allocated by make_array, or returned by other library call
15/// `val` should be a pointer to value allocated using this library
13#[no_mangle]16#[no_mangle]
14pub unsafe extern "C" fn jsonnet_json_array_append(_vm: &State, arr: &mut Val, val: &Val) {17pub unsafe extern "C" fn jsonnet_json_array_append(_vm: &State, arr: &mut Val, val: &Val) {
15 match arr {18 match arr {
26 }29 }
27}30}
2831
32/// Adds the field to the object, bound to value.
33///
34/// This shadows any previous binding of the field.
35///
29/// # Safety36/// # Safety
30///37///
31/// This function is safe if passed name is ok38/// `obj` should be a pointer to object value allocated by `make_object`, or returned by other library call
39/// `name` should be NUL-terminated string
32#[no_mangle]40#[no_mangle]
33pub unsafe extern "C" fn jsonnet_json_object_append(41pub unsafe extern "C" fn jsonnet_json_object_append(
34 _vm: &State,42 _vm: &State,
modifiedbindings/jsonnet/src/vars_tlas.rsdiffbeforeafterboth
44
5use jrsonnet_evaluator::State;5use jrsonnet_evaluator::State;
66
7/// Binds a Jsonnet external variable to the given string.
8///
9/// Argument values are copied so memory should be managed by the caller.
10///
7/// # Safety11/// # Safety
12///
13/// `name`, `code` should be a NUL-terminated strings
8#[no_mangle]14#[no_mangle]
9pub unsafe extern "C" fn jsonnet_ext_var(vm: &State, name: *const c_char, value: *const c_char) {15pub unsafe extern "C" fn jsonnet_ext_var(vm: &State, name: *const c_char, value: *const c_char) {
10 let name = CStr::from_ptr(name);16 let name = CStr::from_ptr(name);
11 let value = CStr::from_ptr(value);17 let value = CStr::from_ptr(value);
18
19 let any_initializer = vm.context_initializer();
20 any_initializer
21 .as_any()
22 .downcast_ref::<jrsonnet_stdlib::ContextInitializer>()
23 .expect("only stdlib context initializer supported")
12 vm.add_ext_str(24 .add_ext_str(
13 name.to_str().unwrap().into(),25 name.to_str().expect("name is not utf-8").into(),
14 value.to_str().unwrap().into(),26 value.to_str().expect("value is not utf-8").into(),
15 )27 )
16}28}
1729
30/// Binds a Jsonnet external variable to the given code.
31///
32/// Argument values are copied so memory should be managed by the caller.
33///
18/// # Safety34/// # Safety
35///
36/// `name`, `code` should be a NUL-terminated strings
19#[no_mangle]37#[no_mangle]
20pub unsafe extern "C" fn jsonnet_ext_code(vm: &State, name: *const c_char, value: *const c_char) {38pub unsafe extern "C" fn jsonnet_ext_code(vm: &State, name: *const c_char, code: *const c_char) {
21 let name = CStr::from_ptr(name);39 let name = CStr::from_ptr(name);
22 let value = CStr::from_ptr(value);40 let code = CStr::from_ptr(code);
41
42 let any_initializer = vm.context_initializer();
43 any_initializer
44 .as_any()
45 .downcast_ref::<jrsonnet_stdlib::ContextInitializer>()
46 .expect("only stdlib context initializer supported")
23 vm.add_ext_code(name.to_str().unwrap(), value.to_str().unwrap().into())47 .add_ext_code(
48 name.to_str().expect("name is not utf-8"),
49 code.to_str().expect("code is not utf-8"),
50 )
24 .unwrap()51 .expect("can't parse ext code")
25}52}
53
54/// Binds a top-level string argument for a top-level parameter.
55///
56/// Argument values are copied so memory should be managed by the caller.
57///
26/// # Safety58/// # Safety
59///
60/// `name`, `value` should be a NUL-terminated strings
27#[no_mangle]61#[no_mangle]
28pub unsafe extern "C" fn jsonnet_tla_var(vm: &State, name: *const c_char, value: *const c_char) {62pub unsafe extern "C" fn jsonnet_tla_var(vm: &State, name: *const c_char, value: *const c_char) {
29 let name = CStr::from_ptr(name);63 let name = CStr::from_ptr(name);
30 let value = CStr::from_ptr(value);64 let value = CStr::from_ptr(value);
31 vm.add_tla_str(65 vm.add_tla_str(
32 name.to_str().unwrap().into(),66 name.to_str().expect("name is not utf-8").into(),
33 value.to_str().unwrap().into(),67 value.to_str().expect("value is not utf-8").into(),
34 )68 )
35}69}
70
71/// Binds a top-level code argument for a top-level parameter.
72///
73/// Argument values are copied so memory should be managed by the caller.
74///
36/// # Safety75/// # Safety
76///
77/// `name`, `code` should be a NUL-terminated strings
37#[no_mangle]78#[no_mangle]
38pub unsafe extern "C" fn jsonnet_tla_code(vm: &State, name: *const c_char, value: *const c_char) {79pub unsafe extern "C" fn jsonnet_tla_code(vm: &State, name: *const c_char, code: *const c_char) {
39 let name = CStr::from_ptr(name);80 let name = CStr::from_ptr(name);
40 let value = CStr::from_ptr(value);81 let code = CStr::from_ptr(code);
41 vm.add_tla_code(name.to_str().unwrap().into(), value.to_str().unwrap())82 vm.add_tla_code(
83 name.to_str().expect("name is not utf-8").into(),
84 code.to_str().expect("code is not utf-8"),
85 )
42 .unwrap()86 .expect("can't parse tla code")
43}87}
4488
deletedbuild/Dockerfilediffbeforeafterboth

no changes

deletedbuild/make-docker.shdiffbeforeafterboth

no changes

deletedbuild/run-docker.shdiffbeforeafterboth

no changes

deletedcmds/jrsonnet-fmt/Cargo.tomldiffbeforeafterboth

no changes

deletedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth

no changes

modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
15 "jrsonnet-evaluator/exp-preserve-order",15 "jrsonnet-evaluator/exp-preserve-order",
16 "jrsonnet-evaluator/exp-serde-preserve-order",16 "jrsonnet-evaluator/exp-serde-preserve-order",
17 "jrsonnet-cli/exp-preserve-order",17 "jrsonnet-cli/exp-preserve-order",
18 "jrsonnet-cli/exp-serde-preserve-order",
18]19]
19# Destructuring of locals20# Destructuring of locals
20exp-destruct = ["jrsonnet-evaluator/exp-destruct"]21exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
22# std.thisFile support
23legacy-this-file = ["jrsonnet-cli/legacy-this-file"]
2124
22[dependencies]25[dependencies]
23jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2" }26jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2" }
2730
28mimallocator = { version = "0.1.3", optional = true }31mimallocator = { version = "0.1.3", optional = true }
29thiserror = "1.0"32thiserror = "1.0"
30clap = { version = "3.1", features = ["derive"] }33clap = { version = "3.2", features = ["derive"] }
31clap_complete = { version = "3.1" }34clap_complete = { version = "3.2" }
3235
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
1use std::{1use std::{
2 env::current_dir,
3 fs::{create_dir_all, File},2 fs::{create_dir_all, File},
4 io::{Read, Write},3 io::{Read, Write},
5};4};
133132
134 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;133 let input = opts.input.input.ok_or(Error::MissingInputArgument)?;
135 let val = if opts.input.exec {134 let val = if opts.input.exec {
136 s.evaluate_snippet("<cmdline>".to_owned(), (&input as &str).into())?135 s.evaluate_snippet("<cmdline>".to_owned(), &input as &str)?
137 } else if input == "-" {136 } else if input == "-" {
138 let mut input = Vec::new();137 let mut input = Vec::new();
139 std::io::stdin().read_to_end(&mut input)?;138 std::io::stdin().read_to_end(&mut input)?;
140 let input_str = std::str::from_utf8(&input)?.into();139 let input_str = std::str::from_utf8(&input)?;
141 s.evaluate_snippet("<stdin>".to_owned(), input_str)?140 s.evaluate_snippet("<stdin>".to_owned(), input_str)?
142 } else {141 } else {
143 s.import(s.resolve_file(&current_dir().expect("cwd"), &input)?)?142 s.import(&input)?
144 };143 };
145144
146 let val = s.with_tla(val)?;145 let val = s.with_tla(val)?;
modifiedcrates/jrsonnet-cli/Cargo.tomldiffbeforeafterboth
88
9[features]9[features]
10exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]10exp-preserve-order = [
11 "jrsonnet-evaluator/exp-preserve-order",
12 "jrsonnet-stdlib/exp-preserve-order",
13]
14exp-serde-preserve-order = [
15 "jrsonnet-evaluator/exp-serde-preserve-order",
16 "jrsonnet-stdlib/exp-serde-preserve-order",
17]
18legacy-this-file = ["jrsonnet-stdlib/legacy-this-file"]
1119
12[dependencies]20[dependencies]
13jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2", features = [21jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2", features = [
14 "explaining-traces",22 "explaining-traces",
15] }23] }
16jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }24jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }
17jrsonnet-gcmodule = { version = "0.3.4" }25jrsonnet-gcmodule = { version = "0.3.4" }
26jrsonnet-stdlib = { path = "../../crates/jrsonnet-stdlib", version = "0.4.2" }
1827
19clap = { version = "3.1", features = ["derive"] }28clap = { version = "3.2", features = ["derive"] }
2029
deletedcrates/jrsonnet-cli/src/ext.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth
1mod ext;1mod manifest;
2mod manifest;2mod stdlib;
3mod tla;3mod tla;
4mod trace;4mod trace;
55
6use std::{env, path::PathBuf};6use std::{env, path::PathBuf};
77
8use clap::Parser;8use clap::Parser;
9pub use ext::*;
10use jrsonnet_evaluator::{error::Result, FileImportResolver, State};9use jrsonnet_evaluator::{error::Result, FileImportResolver, State};
11use jrsonnet_gcmodule::with_thread_object_space;10use jrsonnet_gcmodule::with_thread_object_space;
12pub use manifest::*;11pub use manifest::*;
12pub use stdlib::*;
13pub use tla::*;13pub use tla::*;
14pub use trace::*;14pub use trace::*;
1515
31#[derive(Parser)]31#[derive(Parser)]
32#[clap(next_help_heading = "OPTIONS")]32#[clap(next_help_heading = "OPTIONS")]
33pub struct MiscOpts {33pub struct MiscOpts {
34 /// Disable standard library.
35 /// By default standard library will be available via global `std` variable.
36 /// Note that standard library will still be loaded
37 /// if chosen manifestification method is not `none`.
38 #[clap(long)]
39 no_stdlib: bool,
40
41 /// Maximal allowed number of stack frames,34 /// Maximal allowed number of stack frames,
42 /// stack overflow error will be raised if this number gets exceeded.35 /// stack overflow error will be raised if this number gets exceeded.
52}45}
53impl ConfigureState for MiscOpts {46impl ConfigureState for MiscOpts {
54 fn configure(&self, s: &State) -> Result<()> {47 fn configure(&self, s: &State) -> Result<()> {
55 if !self.no_stdlib {
56 s.with_stdlib();
57 }
58
59 let mut library_paths = self.jpath.clone();48 let mut library_paths = self.jpath.clone();
60 library_paths.reverse();49 library_paths.reverse();
61 if let Some(path) = env::var_os("JSONNET_PATH") {50 if let Some(path) = env::var_os("JSONNET_PATH") {
62 library_paths.extend(env::split_paths(path.as_os_str()));51 library_paths.extend(env::split_paths(path.as_os_str()));
63 }52 }
6453
65 s.set_import_resolver(Box::new(FileImportResolver { library_paths }));54 s.set_import_resolver(Box::new(FileImportResolver::new(library_paths)));
6655
67 s.set_max_stack(self.max_stack);56 s.set_max_stack(self.max_stack);
68 Ok(())57 Ok(())
79 #[clap(flatten)]68 #[clap(flatten)]
80 tla: TLAOpts,69 tla: TLAOpts,
81 #[clap(flatten)]70 #[clap(flatten)]
82 ext: ExtVarOpts,71 std: StdOpts,
8372
84 #[clap(flatten)]73 #[clap(flatten)]
85 trace: TraceOpts,74 trace: TraceOpts,
91 self.trace.configure(s)?;80 self.trace.configure(s)?;
92 self.misc.configure(s)?;81 self.misc.configure(s)?;
93 self.tla.configure(s)?;82 self.tla.configure(s)?;
94 self.ext.configure(s)?;83 self.std.configure(s)?;
95 Ok(())84 Ok(())
96 }85 }
97}86}
113}102}
114impl GcOpts {103impl GcOpts {
115 pub fn stats_printer(&self) -> (Option<GcStatsPrinter>, Option<LeakSpace>) {104 pub fn stats_printer(&self) -> (Option<GcStatsPrinter>, Option<LeakSpace>) {
105 // Constructed structs have side-effects in Drop impl
106 #[allow(clippy::unnecessary_lazy_evaluations)]
116 (107 (
117 self.gc_print_stats.then(|| GcStatsPrinter {108 self.gc_print_stats.then(|| GcStatsPrinter {
118 collect_before_printing_stats: self.gc_collect_before_printing_stats,109 collect_before_printing_stats: self.gc_collect_before_printing_stats,
addedcrates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth
42}42}
43impl ConfigureState for TraceOpts {43impl ConfigureState for TraceOpts {
44 fn configure(&self, s: &State) -> Result<()> {44 fn configure(&self, s: &State) -> Result<()> {
45 let resolver = if let Ok(dir) = std::env::current_dir() {45 let resolver = PathResolver::new_cwd_fallback();
46 PathResolver::Relative(dir)
47 } else {
48 PathResolver::Absolute
49 };
50 match self46 match self
51 .trace_format47 .trace_format
52 .as_ref()48 .as_ref()
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
7edition = "2021"7edition = "2021"
88
9[features]9[features]
10default = ["serialized-stdlib", "explaining-traces", "friendly-errors"]10default = ["explaining-traces", "friendly-errors"]
11# Serializes standard library AST instead of parsing them every run
12serialized-stdlib = ["bincode", "jrsonnet-parser/serde"]
13# Rustc-like trace visualization11# Rustc-like trace visualization
14explaining-traces = ["annotate-snippets"]12explaining-traces = ["annotate-snippets"]
15# Allows library authors to throw custom errors13# Allows library authors to throw custom errors
23exp-serde-preserve-order = ["serde_json/preserve_order"]21exp-serde-preserve-order = ["serde_json/preserve_order"]
24# Implements field destructuring22# Implements field destructuring
25exp-destruct = ["jrsonnet-parser/exp-destruct"]23exp-destruct = ["jrsonnet-parser/exp-destruct"]
24# Provide Typed for conversions to/from serde_json::Value type
25serde_json = ["dep:serde_json"]
2626
27[dependencies]27[dependencies]
28jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }28jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
29jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }29jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
30jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.4.2" }
31jrsonnet-types = { path = "../jrsonnet-types", version = "0.4.2" }30jrsonnet-types = { path = "../jrsonnet-types", version = "0.4.2" }
32jrsonnet-macros = { path = "../jrsonnet-macros", version = "0.4.2" }31jrsonnet-macros = { path = "../jrsonnet-macros", version = "0.4.2" }
33jrsonnet-gcmodule = { version = "0.3.4" }32jrsonnet-gcmodule = { version = "0.3.4" }
36hashbrown = "0.12.1"35hashbrown = "0.12.1"
37static_assertions = "1.1"36static_assertions = "1.1"
3837
39md5 = "0.7.0"
40base64 = "0.13.0"
41rustc-hash = "1.1"38rustc-hash = "1.1"
4239
43thiserror = "1.0"40thiserror = "1.0"
4441
45serde = "1.0"42serde = "1.0"
43# Optional integration
46serde_json = "1.0"44serde_json = { version = "1.0.82", optional = true }
47serde_yaml_with_quirks = "0.8.24"
4845
49anyhow = { version = "1.0", optional = true }46anyhow = { version = "1.0", optional = true }
50# Friendly errors47# Friendly errors
54# Explaining traces51# Explaining traces
55annotate-snippets = { version = "0.9.1", features = ["color"], optional = true }52annotate-snippets = { version = "0.9.1", features = ["color"], optional = true }
56
57[build-dependencies]
58jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.4.2" }
59jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
60serde = "1.0"
61bincode = "1.3"
6253
deletedcrates/jrsonnet-evaluator/build.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
20 }20 }
21}21}
2222
23/// Context keeps information about current lexical code location
24///
25/// This information includes local variables, top-level object (`$`), current object (`this`), and super object (`super`)
23#[derive(Debug, Clone, Trace)]26#[derive(Debug, Clone, Trace)]
24pub struct Context(Cc<ContextInternals>);27pub struct Context(Cc<ContextInternals>);
25impl Context {28impl Context {
139 }142 }
140}143}
144
145pub struct ContextBuilder {
146 bindings: GcHashMap<IStr, Thunk<Val>>,
147 extend: Option<Context>,
148}
149
150impl ContextBuilder {
151 pub fn new() -> Self {
152 Self::with_capacity(0)
153 }
154 pub fn with_capacity(capacity: usize) -> Self {
155 Self {
156 bindings: GcHashMap::with_capacity(capacity),
157 extend: None,
158 }
159 }
160 pub fn extend(parent: Context) -> Self {
161 Self {
162 bindings: GcHashMap::new(),
163 extend: Some(parent),
164 }
165 }
166 /// # Panics
167 /// If `name` is already bound
168 pub fn bind(&mut self, name: IStr, value: Thunk<Val>) -> &mut Self {
169 let old = self.bindings.insert(name, value);
170 assert!(old.is_none(), "variable bound twice in single context call");
171 self
172 }
173 pub fn build(self) -> Context {
174 if let Some(parent) = self.extend {
175 parent.extend(self.bindings, None, None, None)
176 } else {
177 Context(Cc::new(ContextInternals {
178 bindings: LayeredHashMap::new(self.bindings),
179 dollar: None,
180 sup: None,
181 this: None,
182 }))
183 }
184 }
185}
186
187impl Default for ContextBuilder {
188 fn default() -> Self {
189 Self::new()
190 }
191}
141192
modifiedcrates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
22
3use jrsonnet_gcmodule::{Cc, Trace};3use jrsonnet_gcmodule::{Cc, Trace};
44
5// TODO: Replace with OnceCell once in std
5#[derive(Clone, Trace)]6#[derive(Clone, Trace)]
6pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);7pub struct Pending<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);
7impl<T: Trace + 'static> Pending<T> {8impl<T: Trace + 'static> Pending<T> {
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
22
3use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;
4use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;
5use jrsonnet_parser::{BinaryOpType, ExprLocation, Source, UnaryOpType};5use jrsonnet_parser::{BinaryOpType, ExprLocation, Source, SourcePath, UnaryOpType};
6use jrsonnet_types::ValType;6use jrsonnet_types::ValType;
7use thiserror::Error;7use thiserror::Error;
88
9use crate::{9use crate::{stdlib::format::FormatError, typed::TypeLocError};
10 stdlib::{format::FormatError, sort::SortError},
11 typed::TypeLocError,
12};
1310
14fn format_found(list: &[IStr], what: &str) -> String {11fn format_found(list: &[IStr], what: &str) -> String {
35 out32 out
36}33}
34
35fn format_signature(sig: &FunctionSignature) -> String {
36 let mut out = String::new();
37 out.push_str("\nFunction has the following signature: ");
38 out.push('(');
39 if sig.is_empty() {
40 out.push_str("/*no arguments*/");
41 } else {
42 for (i, (name, has_default)) in sig.iter().enumerate() {
43 if i != 0 {
44 out.push_str(", ");
45 }
46 if let Some(name) = name {
47 out.push_str(name);
48 } else {
49 out.push_str("<unnamed>");
50 }
51 if *has_default {
52 out.push_str(" = <default>");
53 }
54 }
55 }
56 out.push(')');
57 out
58}
3759
38const fn format_empty_str(str: &str) -> &str {60const fn format_empty_str(str: &str) -> &str {
39 if str.is_empty() {61 if str.is_empty() {
43 }65 }
44}66}
4567
68type FunctionSignature = Vec<(Option<IStr>, bool)>;
69
70/// Possible errors
71#[allow(missing_docs)]
46#[derive(Error, Debug, Clone, Trace)]72#[derive(Error, Debug, Clone, Trace)]
73#[non_exhaustive]
47pub enum Error {74pub enum Error {
48 #[error("intrinsic not found: {0}")]75 #[error("intrinsic not found: {0}")]
49 IntrinsicNotFound(IStr),76 IntrinsicNotFound(IStr),
76 #[error("duplicate local var: {0}")]103 #[error("duplicate local var: {0}")]
77 DuplicateLocalVar(IStr),104 DuplicateLocalVar(IStr),
78105
79 #[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{}", e)).collect::<Vec<_>>().join(", "))]106 #[error("type mismatch: expected {}, got {2} {0}", .1.iter().map(|e| format!("{e}")).collect::<Vec<_>>().join(", "))]
80 TypeMismatch(&'static str, Vec<ValType>, ValType),107 TypeMismatch(&'static str, Vec<ValType>, ValType),
81 #[error("no such field: {}{}", format_empty_str(.0), format_found(.1, "field"))]108 #[error("no such field: {}{}", format_empty_str(.0), format_found(.1, "field"))]
82 NoSuchField(IStr, Vec<IStr>),109 NoSuchField(IStr, Vec<IStr>),
87 UnknownFunctionParameter(String),114 UnknownFunctionParameter(String),
88 #[error("argument {0} is already bound")]115 #[error("argument {0} is already bound")]
89 BindingParameterASecondTime(IStr),116 BindingParameterASecondTime(IStr),
90 #[error("too many args, function has {0}")]117 #[error("too many args, function has {0}{}", format_signature(.1))]
91 TooManyArgsFunctionHas(usize),118 TooManyArgsFunctionHas(usize, FunctionSignature),
92 #[error("function argument is not passed: {0}")]119 #[error("function argument is not passed: {}{}", .0.as_ref().map_or("<unnamed>", IStr::as_str), format_signature(.1))]
93 FunctionParameterNotBoundInCall(IStr),120 FunctionParameterNotBoundInCall(Option<IStr>, FunctionSignature),
94121
95 #[error("external variable is not defined: {0}")]122 #[error("external variable is not defined: {0}")]
96 UndefinedExternalVariable(IStr),123 UndefinedExternalVariable(IStr),
113 StandaloneSuper,140 StandaloneSuper,
114141
115 #[error("can't resolve {1} from {0}")]142 #[error("can't resolve {1} from {0}")]
116 ImportFileNotFound(PathBuf, String),143 ImportFileNotFound(SourcePath, String),
117 #[error("resolved file not found: {0}")]144 #[error("can't resolve absolute {0}")]
118 ResolvedFileNotFound(PathBuf),145 AbsoluteImportFileNotFound(PathBuf),
146 #[error("resolved file not found: {:?}", .0)]
147 ResolvedFileNotFound(SourcePath),
148 #[error("can't import {0}: is a directory")]
149 ImportIsADirectory(SourcePath),
119 #[error("imported file is not valid utf-8: {0:?}")]150 #[error("imported file is not valid utf-8: {0:?}")]
120 ImportBadFileUtf8(PathBuf),151 ImportBadFileUtf8(SourcePath),
121 #[error("import io error: {0}")]152 #[error("import io error: {0}")]
122 ImportIo(String),153 ImportIo(String),
123 #[error("tried to import {1} from {0}, but imports is not supported")]154 #[error("tried to import {1} from {0}, but imports are not supported")]
124 ImportNotSupported(PathBuf, PathBuf),155 ImportNotSupported(SourcePath, String),
156 #[error("tried to import {0}, but absolute imports are not supported")]
157 AbsoluteImportNotSupported(PathBuf),
125 #[error("can't import from virtual file")]158 #[error("can't import from virtual file")]
126 CantImportFromVirtualFile,159 CantImportFromVirtualFile,
127 #[error(160 #[error(
128 "syntax error: expected {}, got {:?}",161 "syntax error: expected {}, got {:?}",
129 .error.expected,162 .error.expected,
130 .source_code.chars().nth(error.location.offset)163 .path.code().chars().nth(error.location.offset)
131 .map_or_else(|| "EOF".into(), |c| c.to_string())164 .map_or_else(|| "EOF".into(), |c| c.to_string())
132 )]165 )]
133 ImportSyntaxError {166 ImportSyntaxError {
134 path: Source,167 path: Source,
135 source_code: IStr,
136 #[trace(skip)]168 #[trace(skip)]
137 error: Box<jrsonnet_parser::ParseError>,169 error: Box<jrsonnet_parser::ParseError>,
138 },170 },
169 Format(#[from] FormatError),201 Format(#[from] FormatError),
170 #[error("type error: {0}")]202 #[error("type error: {0}")]
171 TypeError(TypeLocError),203 TypeError(TypeLocError),
172 #[error("sort error: {0}")]
173 Sort(#[from] SortError),
174
175 /// Thrown as error, as this is legacy feature, and error here
176 /// is acceptable for defeating object field cache
177 #[error("should not reach outside: std.thisFile")]
178 MagicThisFileUsed,
179204
180 #[cfg(feature = "anyhow-error")]205 #[cfg(feature = "anyhow-error")]
181 #[error(transparent)]206 #[error(transparent)]
195 }220 }
196}221}
197222
223/// Single stack trace frame
198#[derive(Clone, Debug, Trace)]224#[derive(Clone, Debug, Trace)]
199pub struct StackTraceElement {225pub struct StackTraceElement {
226 /// Source of this frame
227 /// Some frames only act as description, without attached source
200 pub location: Option<ExprLocation>,228 pub location: Option<ExprLocation>,
229 /// Frame description
201 pub desc: String,230 pub desc: String,
202}231}
203#[derive(Debug, Clone, Trace)]232#[derive(Debug, Clone, Trace)]
227 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {256 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228 writeln!(f, "{}", self.0 .0)?;257 writeln!(f, "{}", self.0 .0)?;
229 for el in &self.0 .1 .0 {258 for el in &self.0 .1 .0 {
230 writeln!(f, "\t{:?}", el)?;259 writeln!(f, "\t{el:?}")?;
231 }260 }
232 Ok(())261 Ok(())
233 }262 }
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
13 error::Error::*,13 error::Error::*,
14 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},14 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
15 function::{CallLocation, FuncDesc, FuncVal},15 function::{CallLocation, FuncDesc, FuncVal},
16 stdlib::{std_slice, BUILTINS},
17 tb, throw,16 tb, throw,
18 typed::Typed,17 typed::Typed,
19 val::{ArrValue, CachedUnbound, Thunk, ThunkValue},18 val::{ArrValue, CachedUnbound, IndexableVal, Thunk, ThunkValue},
20 Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, State,19 Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, State,
21 Unbound, Val,20 Unbound, Val,
22};21};
270 }269 }
271 }270 }
272 let this = builder.build();271 let this = builder.build();
273 let _ctx = ctx272 fctx.fill(ctx.extend(GcHashMap::new(), None, None, Some(this.clone())));
274 .extend(GcHashMap::new(), None, None, Some(this.clone()))
275 .into_future(fctx);
276 Ok(this)273 Ok(this)
277}274}
278275
357 ctx: Context,354 ctx: Context,
358 value: &LocExpr,355 value: &LocExpr,
359 args: &ArgsDesc,356 args: &ArgsDesc,
360 loc: CallLocation,357 loc: CallLocation<'_>,
361 tailstrict: bool,358 tailstrict: bool,
362) -> Result<Val> {359) -> Result<Val> {
363 let value = evaluate(s.clone(), ctx.clone(), value)?;360 let value = evaluate(s.clone(), ctx.clone(), value)?;
437 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,434 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,
438 Var(name) => s.push(435 Var(name) => s.push(
439 CallLocation::new(loc),436 CallLocation::new(loc),
440 || format!("variable <{}> access", name),437 || format!("variable <{name}> access"),
441 || ctx.binding(name.clone())?.evaluate(s.clone()),438 || ctx.binding(name.clone())?.evaluate(s.clone()),
442 )?,439 )?,
443 Index(value, index) => {440 Index(value, index) => {
447 ) {444 ) {
448 (Val::Obj(v), Val::Str(key)) => s.push(445 (Val::Obj(v), Val::Str(key)) => s.push(
449 CallLocation::new(loc),446 CallLocation::new(loc),
450 || format!("field <{}> access", key),447 || format!("field <{key}> access"),
451 || match v.get(s.clone(), key.clone()) {448 || match v.get(s.clone(), key.clone()) {
452 Ok(Some(v)) => Ok(v),449 Ok(Some(v)) => Ok(v),
453 #[cfg(not(feature = "friendly-errors"))]450 #[cfg(not(feature = "friendly-errors"))]
473 heap.into_iter().map(|(_, v)| v).collect()470 heap.into_iter().map(|(_, v)| v).collect()
474 ))471 ))
475 }472 }
476 Err(e) if matches!(e.error(), MagicThisFileUsed) => {
477 Ok(Val::Str(loc.0.full_path().into()))
478 }
479 Err(e) => Err(e),473 Err(e) => Err(e),
480 },474 },
481 )?,475 )?,
573 Function(params, body) => {567 Function(params, body) => {
574 evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())568 evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())
575 }569 }
576 Intrinsic(name) => Val::Func(FuncVal::StaticBuiltin(
577 BUILTINS
578 .with(|b| b.get(name).copied())
579 .ok_or_else(|| IntrinsicNotFound(name.clone()))?,
580 )),
581 IntrinsicThisFile => return Err(MagicThisFileUsed.into()),
582 IntrinsicId => Val::Func(FuncVal::identity()),
583 AssertExpr(assert, returned) => {570 AssertExpr(assert, returned) => {
584 evaluate_assert(s.clone(), ctx.clone(), assert)?;571 evaluate_assert(s.clone(), ctx.clone(), assert)?;
585 evaluate(s, ctx, returned)?572 evaluate(s, ctx, returned)?
613 }600 }
614 Slice(value, desc) => {601 Slice(value, desc) => {
615 fn parse_idx<T: Typed>(602 fn parse_idx<T: Typed>(
616 loc: CallLocation,603 loc: CallLocation<'_>,
617 s: State,604 s: State,
618 ctx: &Context,605 ctx: &Context,
619 expr: &Option<LocExpr>,606 expr: &Option<LocExpr>,
622 if let Some(value) = expr {609 if let Some(value) = expr {
623 Ok(Some(s.push(610 Ok(Some(s.push(
624 loc,611 loc,
625 || format!("slice {}", desc),612 || format!("slice {desc}"),
626 || T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),613 || T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),
627 )?))614 )?))
628 } else {615 } else {
635622
636 let start = parse_idx(loc, s.clone(), &ctx, &desc.start, "start")?;623 let start = parse_idx(loc, s.clone(), &ctx, &desc.start, "start")?;
637 let end = parse_idx(loc, s.clone(), &ctx, &desc.end, "end")?;624 let end = parse_idx(loc, s.clone(), &ctx, &desc.end, "end")?;
638 let step = parse_idx(loc, s, &ctx, &desc.step, "step")?;625 let step = parse_idx(loc, s.clone(), &ctx, &desc.step, "step")?;
639626
640 std_slice(indexable.into_indexable()?, start, end, step)?627 IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?, s)?
641 }628 }
642 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {629 i @ (Import(path) | ImportStr(path) | ImportBin(path)) => {
643 let tmp = loc.clone().0;630 let tmp = loc.clone().0;
644 let import_location = tmp
645 .path()
646 .map(|p| {
647 let mut p = p.to_owned();
648 p.pop();
649 p
650 })
651 .unwrap_or_default();
652 let resolved_path = s.resolve_file(&import_location, path as &str)?;631 let resolved_path = s.resolve_from(tmp.source_path(), path as &str)?;
653 match i {632 match i {
654 Import(_) => s.push(633 Import(_) => s.push(
655 CallLocation::new(loc),634 CallLocation::new(loc),
656 || format!("import {:?}", path.clone()),635 || format!("import {:?}", path.clone()),
657 || s.import(resolved_path.clone()),636 || s.import_resolved(resolved_path),
658 )?,637 )?,
659 ImportStr(_) => Val::Str(s.import_str(resolved_path)?),638 ImportStr(_) => Val::Str(s.import_resolved_str(resolved_path)?),
660 ImportBin(_) => Val::Arr(ArrValue::Bytes(s.import_bin(resolved_path)?)),639 ImportBin(_) => Val::Arr(ArrValue::Bytes(s.import_resolved_bin(resolved_path)?)),
661 _ => unreachable!(),640 _ => unreachable!(),
662 }641 }
663 }642 }
modifiedcrates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth
21pub fn evaluate_add_op(s: State, a: &Val, b: &Val) -> Result<Val> {21pub fn evaluate_add_op(s: State, a: &Val, b: &Val) -> Result<Val> {
22 use Val::*;22 use Val::*;
23 Ok(match (a, b) {23 Ok(match (a, b) {
24 (Str(a), Str(b)) if a.is_empty() => Val::Str(b.clone()),
25 (Str(a), Str(b)) if b.is_empty() => Val::Str(a.clone()),
24 (Str(v1), Str(v2)) => Str(((**v1).to_owned() + v2).into()),26 (Str(v1), Str(v2)) => Str(((**v1).to_owned() + v2).into()),
2527
26 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)28 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)
27 (Num(a), Str(b)) => Str(format!("{a}{b}").into()),29 (Num(a), Str(b)) => Str(format!("{a}{b}").into()),
28 (Str(a), Num(b)) => Str(format!("{a}{b}").into()),30 (Str(a), Num(b)) => Str(format!("{a}{b}").into()),
2931
32 (Str(a), o) | (o, Str(a)) if a.is_empty() => Val::Str(o.clone().to_string(s)?),
30 (Str(a), o) => Str(format!("{}{}", a, o.clone().to_string(s)?).into()),33 (Str(a), o) => Str(format!("{a}{}", o.clone().to_string(s)?).into()),
31 (o, Str(a)) => Str(format!("{}{}", o.clone().to_string(s)?, a).into()),34 (o, Str(a)) => Str(format!("{}{a}", o.clone().to_string(s)?).into()),
3235
33 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),36 (Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),
34 (Arr(a), Arr(b)) => {37 (Arr(a), Arr(b)) => {
modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
96 fn named_names(&self, handler: &mut dyn FnMut(&IStr));96 fn named_names(&self, handler: &mut dyn FnMut(&IStr));
97}97}
98
99impl ArgsLike for Vec<Val> {
100 fn unnamed_len(&self) -> usize {
101 self.len()
102 }
103 fn unnamed_iter(
104 &self,
105 _s: State,
106 _ctx: Context,
107 _tailstrict: bool,
108 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,
109 ) -> Result<()> {
110 for (idx, el) in self.iter().enumerate() {
111 handler(idx, Thunk::evaluated(el.clone()))?;
112 }
113 Ok(())
114 }
115 fn named_iter(
116 &self,
117 _s: State,
118 _ctx: Context,
119 _tailstrict: bool,
120 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,
121 ) -> Result<()> {
122 Ok(())
123 }
124 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
125}
98126
99impl ArgsLike for ArgsDesc {127impl ArgsLike for ArgsDesc {
100 fn unnamed_len(&self) -> usize {128 fn unnamed_len(&self) -> usize {
273 }301 }
274}302}
275impl_args_like! {303impl_args_like! {
304 // First argument is already in position, so count starts from 1
276 0usize; A @ B C D E F G H I J K L305 1usize; A @ B C D E F G H I J K L
277}306}
278307
279impl ArgsLike for () {308impl ArgsLike for () {
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
99
10#[derive(Clone, Trace)]10#[derive(Clone, Trace)]
11pub struct BuiltinParam {11pub struct BuiltinParam {
12 /// Parameter name for named call parsing
12 pub name: BuiltinParamName,13 pub name: Option<BuiltinParamName>,
14 /// Is implementation allowed to return empty value
13 pub has_default: bool,15 pub has_default: bool,
14}16}
1517
16/// Do not implement it directly, instead use #[builtin] macro18/// Description of function defined by native code
19///
20/// Prefer to use #[builtin] macro, instead of manual implementation of this trait
17pub trait Builtin: Trace {21pub trait Builtin: Trace {
22 /// Function name to be used in stack traces
18 fn name(&self) -> &str;23 fn name(&self) -> &str;
24 /// Parameter names for named calls
19 fn params(&self) -> &[BuiltinParam];25 fn params(&self) -> &[BuiltinParam];
26 /// Call the builtin
20 fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;27 fn call(
28 &self,
29 s: State,
30 ctx: Context,
31 loc: CallLocation<'_>,
32 args: &dyn ArgsLike,
33 ) -> Result<Val>;
21}34}
35}48}
36impl NativeCallback {49impl NativeCallback {
37 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]50 #[deprecated = "prefer using builtins directly, use this interface only for bindings"]
38 pub fn new(params: Vec<BuiltinParam>, handler: TraceBox<dyn NativeCallbackHandler>) -> Self {51 pub fn new(
52 params: Vec<Cow<'static, str>>,
53 handler: TraceBox<dyn NativeCallbackHandler>,
54 ) -> Self {
39 Self { params, handler }55 Self {
56 params: params
57 .into_iter()
58 .map(|n| BuiltinParam {
59 name: Some(n),
60 has_default: false,
61 })
62 .collect(),
63 handler,
64 }
40 }65 }
41}66}
80 &self,
81 s: State,
82 ctx: Context,
83 _loc: CallLocation<'_>,
84 args: &dyn ArgsLike,
85 ) -> Result<Val> {
55 let args = parse_builtin_call(s.clone(), ctx, &self.params, args, true)?;86 let args = parse_builtin_call(s.clone(), ctx, &self.params, args, true)?;
56 let mut out_args = Vec::with_capacity(self.params.len());87 let args = args
57 for p in &self.params {
58 out_args.push(args[&p.name].evaluate(s.clone())?);88 .into_iter()
59 }89 .map(|a| a.expect("legacy natives have no default params"))
90 .map(|a| a.evaluate(s.clone()))
91 .collect::<Result<Vec<Val>>>()?;
60 self.handler.call(s, &out_args)92 self.handler.call(s, &args)
61 }93 }
62}94}
6395
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
18pub mod native;18pub mod native;
19pub mod parse;19pub mod parse;
2020
21/// Function callsite location.
22/// Either from other jsonnet code, specified by expression location, or from native (without location).
21#[derive(Clone, Copy)]23#[derive(Clone, Copy)]
22pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);24pub struct CallLocation<'l>(pub Option<&'l ExprLocation>);
23impl<'l> CallLocation<'l> {25impl<'l> CallLocation<'l> {
26 /// Construct new location for calls coming from specified jsonnet expression location.
24 pub const fn new(loc: &'l ExprLocation) -> Self {27 pub const fn new(loc: &'l ExprLocation) -> Self {
25 Self(Some(loc))28 Self(Some(loc))
26 }29 }
27}30}
28impl CallLocation<'static> {31impl CallLocation<'static> {
32 /// Construct new location for calls coming from native code.
29 pub const fn native() -> Self {33 pub const fn native() -> Self {
30 Self(None)34 Self(None)
31 }35 }
32}36}
3337
34/// Function implemented in jsonnet38/// Represents Jsonnet function defined in code.
35#[derive(Debug, PartialEq, Trace)]39#[derive(Debug, PartialEq, Trace)]
36pub struct FuncDesc {40pub struct FuncDesc {
37 /// In expressions like41 /// # Example
42 ///
43 /// In expressions like this, deducted to `a`, unspecified otherwise.
38 /// ```jsonnet44 /// ```jsonnet
39 /// local a = function() ...45 /// local a = function() ...
40 /// local a() ...46 /// local a() ...
41 /// { a: function() ... }47 /// { a: function() ... }
42 /// { a() = ... }48 /// { a() = ... }
43 /// ```49 /// ```
44 ///
45 /// Deducted to `a`, unspecified otherwise
46 pub name: IStr,50 pub name: IStr,
47 /// Context, in which this function was evaluated51 /// Context, in which this function was evaluated.
48 ///52 ///
53 /// # Example
49 /// I.e in54 /// In
50 /// ```jsonnet55 /// ```jsonnet
51 /// local a = 2;56 /// local a = 2;
52 /// function() ...57 /// function() ...
53 /// ```58 /// ```
54 /// context will contain `a`59 /// context will contain `a`.
55 pub ctx: Context,60 pub ctx: Context,
5661
62 /// Function parameter definition
57 pub params: ParamsDesc,63 pub params: ParamsDesc,
64 /// Function body
58 pub body: LocExpr,65 pub body: LocExpr,
59}66}
60impl FuncDesc {67impl FuncDesc {
82 }89 }
83}90}
8491
85/// Any possible function value, including plain functions and user-provided builtins92/// Represents a Jsonnet function value, including plain functions and user-provided builtins.
86#[allow(clippy::module_name_repetitions)]93#[allow(clippy::module_name_repetitions)]
87#[derive(Trace, Clone)]94#[derive(Trace, Clone)]
88pub enum FuncVal {95pub enum FuncVal {
89 /// std.id96 /// Identity function, kept this way for comparsions.
90 Id,97 Id,
91 /// Plain function implemented in jsonnet98 /// Plain function implemented in jsonnet.
92 Normal(Cc<FuncDesc>),99 Normal(Cc<FuncDesc>),
93 /// Standard library function100 /// Standard library function.
94 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),101 StaticBuiltin(#[trace(skip)] &'static dyn StaticBuiltin),
95 /// User-provided function102 /// User-provided function.
96 Builtin(Cc<TraceBox<dyn Builtin>>),103 Builtin(Cc<TraceBox<dyn Builtin>>),
97}104}
98105
110}117}
111118
112impl FuncVal {119impl FuncVal {
113 pub fn into_native<D: NativeDesc>(self) -> D::Value {120 /// Amount of non-default required arguments
114 D::into_native(self)
115 }
116 pub fn params_len(&self) -> usize {121 pub fn params_len(&self) -> usize {
117 match self {122 match self {
118 Self::Id => 1,123 Self::Id => 1,
121 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),126 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),
122 }127 }
123 }128 }
129 /// Function name, as defined in code.
124 pub fn name(&self) -> IStr {130 pub fn name(&self) -> IStr {
125 match self {131 match self {
126 Self::Id => "id".into(),132 Self::Id => "id".into(),
129 Self::Builtin(builtin) => builtin.name().into(),135 Self::Builtin(builtin) => builtin.name().into(),
130 }136 }
131 }137 }
138 /// Call function using arguments evaluated in specified `call_ctx` [`Context`].
139 ///
140 /// If `tailstrict` is specified - then arguments will be evaluated before being passed to function body.
132 pub fn evaluate(141 pub fn evaluate(
133 &self,142 &self,
134 s: State,143 s: State,
135 call_ctx: Context,144 call_ctx: Context,
136 loc: CallLocation,145 loc: CallLocation<'_>,
137 args: &dyn ArgsLike,146 args: &dyn ArgsLike,
138 tailstrict: bool,147 tailstrict: bool,
139 ) -> Result<Val> {148 ) -> Result<Val> {
156 Self::Builtin(b) => b.call(s, call_ctx, loc, args),165 Self::Builtin(b) => b.call(s, call_ctx, loc, args),
157 }166 }
158 }167 }
168 /// Helper method, which calls [`Self::evaluate`] with sensible defaults for native code.
159 pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result<Val> {169 pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result<Val> {
160 self.evaluate(s, Context::default(), CallLocation::native(), args, true)170 self.evaluate(s, Context::default(), CallLocation::native(), args, true)
161 }171 }
162172 /// Convert jsonnet function to plain `Fn` value.
173 pub fn into_native<D: NativeDesc>(self) -> D::Value {
174 D::into_native(self)
175 }
176
177 /// Is this function an indentity function.
178 ///
179 /// Currently only works for builtin `std.id`, aka `Self::Id` value, `function(x) x` defined by jsonnet will not count as identity.
163 pub const fn is_identity(&self) -> bool {180 pub const fn is_identity(&self) -> bool {
164 matches!(self, Self::Id)181 matches!(self, Self::Id)
165 }182 }
183 /// Identity function value.
166 pub const fn identity() -> Self {184 pub const fn identity() -> Self {
167 Self::Id185 Self::Id
168 }186 }
modifiedcrates/jrsonnet-evaluator/src/function/native.rsdiffbeforeafterboth
1use super::{arglike::ArgLike, CallLocation, FuncVal};1use super::{arglike::ArgLike, CallLocation, FuncVal};
2use crate::{error::Result, typed::Typed, State};2use crate::{error::Result, typed::Typed, Context, State};
33
4pub trait NativeDesc {4pub trait NativeDesc {
5 type Value;5 type Value;
19 Box::new(move |s: State, $($gen),*| {19 Box::new(move |s: State, $($gen),*| {
20 let val = val.evaluate(20 let val = val.evaluate(
21 s.clone(),21 s.clone(),
22 // This isn't intended to be used with ArgsDesc
22 s.create_default_context(),23 Context::default(),
23 CallLocation::native(),24 CallLocation::native(),
24 &($($gen,)*),25 &($($gen,)*),
25 true26 true
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
1use std::mem::replace;
2
1use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;
2use jrsonnet_interner::IStr;4use jrsonnet_interner::IStr;
3use jrsonnet_parser::{LocExpr, ParamsDesc};5use jrsonnet_parser::{LocExpr, ParamsDesc};
46
5use super::{7use super::{arglike::ArgsLike, builtin::BuiltinParam};
6 arglike::ArgsLike,
7 builtin::{BuiltinParam, BuiltinParamName},
8};
9use crate::{8use crate::{
10 destructure::destruct,9 destructure::destruct,
49 let mut passed_args = GcHashMap::with_capacity(params.len());48 let mut passed_args = GcHashMap::with_capacity(params.len());
50 if args.unnamed_len() > params.len() {49 if args.unnamed_len() > params.len() {
51 throw!(TooManyArgsFunctionHas(params.len()))50 throw!(TooManyArgsFunctionHas(
51 params.len(),
52 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
53 ))
52 }54 }
5355
122 });124 });
123 if !found {125 if !found {
124 throw!(FunctionParameterNotBoundInCall(126 throw!(FunctionParameterNotBoundInCall(
125 param127 param.0.clone().name(),
126 .0
127 .clone()
128 .name()
129 .unwrap_or_else(|| "<destruct>".into())128 params.iter().map(|p| (p.0.name(), p.1.is_some())).collect()
130 ));129 ));
131 }130 }
132 }131 }
156 params: &[BuiltinParam],155 params: &[BuiltinParam],
157 args: &dyn ArgsLike,156 args: &dyn ArgsLike,
158 tailstrict: bool,157 tailstrict: bool,
159) -> Result<GcHashMap<BuiltinParamName, Thunk<Val>>> {158) -> Result<Vec<Option<Thunk<Val>>>> {
160 let mut passed_args = GcHashMap::with_capacity(params.len());159 let mut passed_args: Vec<Option<Thunk<Val>>> = vec![None; params.len()];
161 if args.unnamed_len() > params.len() {160 if args.unnamed_len() > params.len() {
162 throw!(TooManyArgsFunctionHas(params.len()))161 throw!(TooManyArgsFunctionHas(
162 params.len(),
163 params
164 .iter()
165 .map(|p| (p.name.as_ref().map(|v| v.as_ref().into()), p.has_default))
166 .collect()
167 ))
163 }168 }
164169
165 let mut filled_args = 0;170 let mut filled_args = 0;
166171
167 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {172 args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
168 let name = params[id].name.clone();173 passed_args[id] = Some(arg);
169 passed_args.insert(name, arg);
170 filled_args += 1;174 filled_args += 1;
171 Ok(())175 Ok(())
172 })?;176 })?;
173177
174 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {178 args.named_iter(s, ctx, tailstrict, &mut |name, arg| {
175 // FIXME: O(n) for arg existence check179 // FIXME: O(n) for arg existence check
176 let p = params180 let id = params
177 .iter()181 .iter()
178 .find(|p| p.name == name as &str)182 .position(|p| p.name.as_ref().map_or(false, |v| v as &str == name as &str))
179 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;183 .ok_or_else(|| UnknownFunctionParameter((name as &str).to_owned()))?;
180 if passed_args.insert(p.name.clone(), arg).is_some() {184 if replace(&mut passed_args[id], Some(arg)).is_some() {
181 throw!(BindingParameterASecondTime(name.clone()));185 throw!(BindingParameterASecondTime(name.clone()));
182 }186 }
183 filled_args += 1;187 filled_args += 1;
184 Ok(())188 Ok(())
185 })?;189 })?;
186190
187 if filled_args < params.len() {191 if filled_args < params.len() {
188 for param in params.iter().filter(|p| p.has_default) {192 for (id, _) in params.iter().enumerate().filter(|(_, p)| p.has_default) {
189 if passed_args.contains_key(&param.name) {193 if passed_args[id].is_some() {
190 continue;194 continue;
191 }195 }
192 filled_args += 1;196 filled_args += 1;
197 for param in params.iter().skip(args.unnamed_len()) {201 for param in params.iter().skip(args.unnamed_len()) {
198 let mut found = false;202 let mut found = false;
199 args.named_names(&mut |name| {203 args.named_names(&mut |name| {
200 if name as &str == &param.name as &str {204 if param
205 .name
206 .as_ref()
207 .map_or(false, |v| v as &str == name as &str)
208 {
201 found = true;209 found = true;
202 }210 }
203 });211 });
204 if !found {212 if !found {
205 throw!(FunctionParameterNotBoundInCall(param.name.clone().into()));213 throw!(FunctionParameterNotBoundInCall(
214 param.name.as_ref().map(|v| v.as_ref().into()),
215 params
216 .iter()
217 .map(|p| (p.name.as_ref().map(|p| p.as_ref().into()), p.has_default))
218 .collect()
219 ));
206 }220 }
207 }221 }
215/// and with unbound values causing error to be returned229/// and with unbound values causing error to be returned
216pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {230pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Result<Context> {
217 #[derive(Trace)]231 #[derive(Trace)]
218 struct DependsOnUnbound(IStr);232 struct DependsOnUnbound(IStr, ParamsDesc);
219 impl ThunkValue for DependsOnUnbound {233 impl ThunkValue for DependsOnUnbound {
220 type Output = Val;234 type Output = Val;
221 fn get(self: Box<Self>, _: State) -> Result<Val> {235 fn get(self: Box<Self>, _: State) -> Result<Val> {
222 Err(FunctionParameterNotBoundInCall(self.0.clone()).into())236 Err(FunctionParameterNotBoundInCall(
237 Some(self.0.clone()),
238 self.1.iter().map(|p| (p.0.name(), p.1.is_some())).collect(),
239 )
240 .into())
223 }241 }
243 destruct(261 destruct(
244 &param.0,262 &param.0,
245 Thunk::new(tb!(DependsOnUnbound(263 Thunk::new(tb!(DependsOnUnbound(
246 param.0.name().unwrap_or_else(|| "<destruct>".into())264 param.0.name().unwrap_or_else(|| "<destruct>".into()),
265 params.clone()
247 ))),266 ))),
248 fctx.clone(),267 fctx.clone(),
249 &mut bindings,268 &mut bindings,
modifiedcrates/jrsonnet-evaluator/src/gc.rsdiffbeforeafterboth
22}22}
2323
24impl<T: ?Sized + Trace> Trace for TraceBox<T> {24impl<T: ?Sized + Trace> Trace for TraceBox<T> {
25 fn trace(&self, tracer: &mut Tracer) {25 fn trace(&self, tracer: &mut Tracer<'_>) {
26 self.0.trace(tracer);26 self.0.trace(tracer);
27 }27 }
2828
5353
54impl<T: ?Sized> Borrow<T> for TraceBox<T> {54impl<T: ?Sized> Borrow<T> for TraceBox<T> {
55 fn borrow(&self) -> &T {55 fn borrow(&self) -> &T {
56 &*self.056 &self.0
57 }57 }
58}58}
5959
60impl<T: ?Sized> BorrowMut<T> for TraceBox<T> {60impl<T: ?Sized> BorrowMut<T> for TraceBox<T> {
61 fn borrow_mut(&mut self) -> &mut T {61 fn borrow_mut(&mut self) -> &mut T {
62 &mut *self.062 &mut self.0
63 }63 }
64}64}
6565
66impl<T: ?Sized> AsRef<T> for TraceBox<T> {66impl<T: ?Sized> AsRef<T> for TraceBox<T> {
67 fn as_ref(&self) -> &T {67 fn as_ref(&self) -> &T {
68 &*self.068 &self.0
69 }69 }
70}70}
7171
72impl<T: ?Sized> AsMut<T> for TraceBox<T> {72impl<T: ?Sized> AsMut<T> for TraceBox<T> {
73 fn as_mut(&mut self) -> &mut T {73 fn as_mut(&mut self) -> &mut T {
74 &mut *self.074 &mut self.0
75 }75 }
76}76}
7777
92where92where
93 V: Trace,93 V: Trace,
94{94{
95 fn trace(&self, tracer: &mut jrsonnet_gcmodule::Tracer) {95 fn trace(&self, tracer: &mut Tracer<'_>) {
96 for v in &self.0 {96 for v in &self.0 {
97 v.trace(tracer);97 v.trace(tracer);
98 }98 }
133 K: Trace,133 K: Trace,
134 V: Trace,134 V: Trace,
135{135{
136 fn trace(&self, tracer: &mut jrsonnet_gcmodule::Tracer) {136 fn trace(&self, tracer: &mut Tracer<'_>) {
137 for (k, v) in &self.0 {137 for (k, v) in &self.0 {
138 k.trace(tracer);138 k.trace(tracer);
139 v.trace(tracer);139 v.trace(tracer);
modifiedcrates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth
1use std::{1use std::{
2 any::Any,2 any::Any,
3 cell::RefCell,
4 env::current_dir,
3 fs,5 fs,
4 io::Read,6 io::{ErrorKind, Read},
5 path::{Path, PathBuf},7 path::{Path, PathBuf},
6};8};
79
8use fs::File;10use fs::File;
11use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};
912
10use crate::{13use crate::{
11 error::{Error::*, Result},14 error::{
15 Error::{self, *},
16 Result,
17 },
12 throw,18 throw,
13};19};
1420
15/// Implements file resolution logic for `import` and `importStr`21/// Implements file resolution logic for `import` and `importStr`
16pub trait ImportResolver {22pub trait ImportResolver {
17 /// Resolves real file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond23 /// Resolves file path, e.g. `(/home/user/manifests, b.libjsonnet)` can correspond
18 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`24 /// both to `/home/user/manifests/b.libjsonnet` and to `/home/user/${vendor}/b.libjsonnet`
19 /// where `${vendor}` is a library path.25 /// where `${vendor}` is a library path.
26 ///
27 /// `from` should only be returned from [`ImportResolver::resolve`], or from other defined file, any other value
28 /// may result in panic
20 fn resolve_file(&self, from: &Path, path: &str) -> Result<PathBuf>;29 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
2130 throw!(ImportNotSupported(from.clone(), path.into()))
31 }
32 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {
33 self.resolve_from(&SourcePath::default(), path)
34 }
35 /// Resolves absolute path, doesn't supports jpath and other fancy things
36 fn resolve(&self, path: &Path) -> Result<SourcePath> {
37 throw!(AbsoluteImportNotSupported(path.to_owned()))
38 }
39
40 /// Load resolved file
41 /// This should only be called with value returned from [`ImportResolver::resolve_file`]/[`ImportResolver::resolve`],
42 /// this cannot be resolved using associated type, as evaluator uses object instead of generic for [`ImportResolver`]
22 fn load_file_contents(&self, resolved: &Path) -> Result<Vec<u8>>;43 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>>;
2344
24 /// # Safety45 /// For downcasts
25 ///
26 /// For use only in bindings, should not be used elsewhere.
27 /// Implementations which are not intended to be used in bindings
28 /// should panic on call to this method.
29 unsafe fn as_any(&self) -> &dyn Any;46 fn as_any(&self) -> &dyn Any;
30}47}
3148
32/// Dummy resolver, can't resolve/load any file49/// Dummy resolver, can't resolve/load any file
33pub struct DummyImportResolver;50pub struct DummyImportResolver;
34impl ImportResolver for DummyImportResolver {51impl ImportResolver for DummyImportResolver {
35 fn resolve_file(&self, from: &Path, path: &str) -> Result<PathBuf> {
36 throw!(ImportNotSupported(from.into(), path.into()))
37 }
38
39 fn load_file_contents(&self, _resolved: &Path) -> Result<Vec<u8>> {52 fn load_file_contents(&self, _resolved: &SourcePath) -> Result<Vec<u8>> {
40 panic!("dummy resolver can't load any file")53 panic!("dummy resolver can't load any file")
41 }54 }
4255
43 unsafe fn as_any(&self) -> &dyn Any {56 fn as_any(&self) -> &dyn Any {
44 panic!("`as_any($self)` is not supported by dummy resolver")57 self
45 }58 }
46}59}
47#[allow(clippy::use_self)]60#[allow(clippy::use_self)]
56pub struct FileImportResolver {69pub struct FileImportResolver {
57 /// Library directories to search for file.70 /// Library directories to search for file.
58 /// Referred to as `jpath` in original jsonnet implementation.71 /// Referred to as `jpath` in original jsonnet implementation.
59 pub library_paths: Vec<PathBuf>,72 library_paths: RefCell<Vec<PathBuf>>,
60}73}
74impl FileImportResolver {
75 pub fn new(jpath: Vec<PathBuf>) -> Self {
76 Self {
77 library_paths: RefCell::new(jpath),
78 }
79 }
80 /// Dynamically add new jpath, used by bindings
81 pub fn add_jpath(&self, path: PathBuf) {
82 self.library_paths.borrow_mut().push(path);
83 }
84}
61impl ImportResolver for FileImportResolver {85impl ImportResolver for FileImportResolver {
62 fn resolve_file(&self, from: &Path, path: &str) -> Result<PathBuf> {86 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
63 let mut direct = from.to_path_buf();87 let mut direct = if let Some(f) = from.downcast_ref::<SourceFile>() {
88 let mut o = f.path().to_owned();
89 o.pop();
90 o
91 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {
92 d.path().to_owned()
93 } else if from.is_default() {
94 current_dir().map_err(|e| Error::ImportIo(e.to_string()))?
95 } else {
96 unreachable!("resolver can't return this path")
97 };
64 direct.push(path);98 direct.push(path);
65 if direct.exists() {99 if direct.is_file() {
66 Ok(direct.canonicalize().map_err(|e| ImportIo(e.to_string()))?)100 Ok(SourcePath::new(SourceFile::new(
101 direct.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
102 )))
67 } else {103 } else {
68 for library_path in &self.library_paths {104 for library_path in self.library_paths.borrow().iter() {
69 let mut cloned = library_path.clone();105 let mut cloned = library_path.clone();
70 cloned.push(path);106 cloned.push(path);
71 if cloned.exists() {107 if cloned.exists() {
72 return Ok(cloned.canonicalize().map_err(|e| ImportIo(e.to_string()))?);108 return Ok(SourcePath::new(SourceFile::new(
109 cloned.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
110 )));
73 }111 }
74 }112 }
75 throw!(ImportFileNotFound(from.to_owned(), path.to_owned()))113 throw!(ImportFileNotFound(from.clone(), path.to_owned()))
76 }114 }
77 }115 }
116 fn resolve(&self, path: &Path) -> Result<SourcePath> {
117 let meta = match fs::metadata(path) {
118 Ok(v) => v,
119 Err(e) if e.kind() == ErrorKind::NotFound => {
120 throw!(AbsoluteImportFileNotFound(path.to_owned()))
121 }
122 Err(e) => throw!(Error::ImportIo(e.to_string())),
123 };
124 if meta.is_file() {
125 Ok(SourcePath::new(SourceFile::new(
126 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
127 )))
128 } else if meta.is_dir() {
129 Ok(SourcePath::new(SourceDirectory::new(
130 path.canonicalize().map_err(|e| ImportIo(e.to_string()))?,
131 )))
132 } else {
133 unreachable!("this can't be a symlink")
134 }
135 }
78136
79 fn load_file_contents(&self, id: &Path) -> Result<Vec<u8>> {137 fn load_file_contents(&self, id: &SourcePath) -> Result<Vec<u8>> {
138 let path = if let Some(f) = id.downcast_ref::<SourceFile>() {
139 f.path()
140 } else if id.downcast_ref::<SourceDirectory>().is_some() || id.is_default() {
141 throw!(Error::ImportIsADirectory(id.clone()))
142 } else {
143 unreachable!("other types are not supported in resolve");
144 };
80 let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.to_owned()))?;145 let mut file = File::open(path).map_err(|_e| ResolvedFileNotFound(id.clone()))?;
81 let mut out = Vec::new();146 let mut out = Vec::new();
82 file.read_to_end(&mut out)147 file.read_to_end(&mut out)
83 .map_err(|e| ImportIo(e.to_string()))?;148 .map_err(|e| ImportIo(e.to_string()))?;
84 Ok(out)149 Ok(out)
85 }150 }
151
86 unsafe fn as_any(&self) -> &dyn Any {152 fn as_any(&self) -> &dyn Any {
153 self
154 }
155
156 fn resolve_from_default(&self, path: &str) -> Result<SourcePath> {
87 panic!("this resolver can't be used as any")157 self.resolve_from(&SourcePath::default(), path)
88 }158 }
89}159}
90160
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
16 Self::Null => Val::Null,16 Self::Null => Val::Null,
17 Self::Bool(v) => Val::Bool(v),17 Self::Bool(v) => Val::Bool(v),
18 Self::Number(n) => Val::Num(n.as_f64().ok_or_else(|| {18 Self::Number(n) => Val::Num(n.as_f64().ok_or_else(|| {
19 RuntimeError(format!("json number can't be represented as jsonnet: {}", n).into())19 RuntimeError(format!("json number can't be represented as jsonnet: {n}").into())
20 })?),20 })?),
21 Self::String(s) => Val::Str((&s as &str).into()),21 Self::String(s) => Val::Str((&s as &str).into()),
22 Self::Array(a) => {22 Self::Array(a) => {
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
1//! jsonnet interpreter implementation
2
3#![deny(unsafe_op_in_unsafe_fn)]
1#![warn(clippy::all, clippy::nursery, clippy::pedantic)]4#![warn(
5 clippy::all,
6 clippy::nursery,
7 clippy::pedantic,
8 // missing_docs,
9 elided_lifetimes_in_paths,
10 explicit_outlives_requirements,
11 noop_method_call,
12 single_use_lifetimes,
13 variant_size_differences,
14 rustdoc::all
15)]
2#![allow(16#![allow(
3 macro_expanded_macro_exports_accessed_by_absolute_paths,17 macro_expanded_macro_exports_accessed_by_absolute_paths,
37mod integrations;51mod integrations;
38mod map;52mod map;
39mod obj;53mod obj;
40mod stdlib;54pub mod stdlib;
41pub mod trace;55pub mod trace;
42pub mod typed;56pub mod typed;
43pub mod val;57pub mod val;
4458
45use std::{59use std::{
46 borrow::Cow,60 any::Any,
47 cell::{Ref, RefCell, RefMut},61 cell::{Ref, RefCell, RefMut},
48 collections::HashMap,62 collections::HashMap,
49 fmt::{self, Debug},63 fmt::{self, Debug},
50 path::{Path, PathBuf},64 path::Path,
51 rc::Rc,65 rc::Rc,
52};66};
5367
54pub use ctx::*;68pub use ctx::*;
55pub use dynamic::*;69pub use dynamic::*;
56use error::{Error::*, LocError, Result, StackTraceElement};70use error::{Error::*, LocError, Result, StackTraceElement};
57pub use evaluate::*;71pub use evaluate::*;
58use function::{builtin::Builtin, CallLocation, TlaArg};72use function::{CallLocation, TlaArg};
59use gc::{GcHashMap, TraceBox};73use gc::{GcHashMap, TraceBox};
60use hashbrown::hash_map::RawEntryMut;74use hashbrown::hash_map::RawEntryMut;
61pub use import::*;75pub use import::*;
62use jrsonnet_gcmodule::{Cc, Trace};76use jrsonnet_gcmodule::{Cc, Trace};
63use jrsonnet_interner::IBytes;
64pub use jrsonnet_interner::IStr;77pub use jrsonnet_interner::{IBytes, IStr};
65pub use jrsonnet_parser as parser;78pub use jrsonnet_parser as parser;
66use jrsonnet_parser::*;79use jrsonnet_parser::*;
67pub use obj::*;80pub use obj::*;
68use trace::{location_to_offset, offset_to_location, CodeLocation, CompactFormat, TraceFormat};81use trace::{CompactFormat, TraceFormat};
69pub use val::{ManifestFormat, Thunk, Val};82pub use val::{ManifestFormat, Thunk, Val};
7083
84/// Thunk without bound `super`/`this`
85/// object inheritance may be overriden multiple times, and will be fixed only on field read
71pub trait Unbound: Trace {86pub trait Unbound: Trace {
87 /// Type of value after object context is bound
72 type Bound;88 type Bound;
89 /// Create value bound to specified object context
73 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;90 fn bind(&self, s: State, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<Self::Bound>;
74}91}
7592
93/// Object fields may, or may not depend on `this`/`super`, this enum allows cheaper reuse of object-independent fields for native code
94/// Standard jsonnet fields are always unbound
76#[derive(Clone, Trace)]95#[derive(Clone, Trace)]
77pub enum LazyBinding {96pub enum MaybeUnbound {
97 /// Value needs to be bound to `this`/`super`
78 Bindable(Cc<TraceBox<dyn Unbound<Bound = Thunk<Val>>>>),98 Unbound(Cc<TraceBox<dyn Unbound<Bound = Thunk<Val>>>>),
99 /// Value is object-independent
79 Bound(Thunk<Val>),100 Bound(Thunk<Val>),
80}101}
81102
82impl Debug for LazyBinding {103impl Debug for MaybeUnbound {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "LazyBinding")105 write!(f, "MaybeUnbound")
85 }106 }
86}107}
87impl LazyBinding {108impl MaybeUnbound {
109 /// Attach object context to value, if required
88 pub fn evaluate(110 pub fn evaluate(
89 &self,111 &self,
90 s: State,112 s: State,
91 sup: Option<ObjValue>,113 sup: Option<ObjValue>,
92 this: Option<ObjValue>,114 this: Option<ObjValue>,
93 ) -> Result<Thunk<Val>> {115 ) -> Result<Thunk<Val>> {
94 match self {116 match self {
95 Self::Bindable(v) => v.bind(s, sup, this),117 Self::Unbound(v) => v.bind(s, sup, this),
96 Self::Bound(v) => Ok(v.clone()),118 Self::Bound(v) => Ok(v.clone()),
97 }119 }
98 }120 }
99}121}
100122
123/// During import, this trait will be called to create initial context for file.
124/// It may initialize global variables, stdlib for example.
125pub trait ContextInitializer {
126 /// Initialize default file context.
127 fn initialize(&self, state: State, for_file: Source) -> Context;
128 /// Allows upcasting from abstract to concrete context initializer.
129 /// jrsonnet by itself doesn't use this method, it is allowed for it to panic.
130 fn as_any(&self) -> &dyn Any;
131}
132
133/// Context initializer which adds nothing.
134pub struct DummyContextInitializer;
135impl ContextInitializer for DummyContextInitializer {
136 fn initialize(&self, _state: State, _for_file: Source) -> Context {
137 Context::default()
138 }
139 fn as_any(&self) -> &dyn Any {
140 self
141 }
142}
143
144/// Dynamically reconfigurable evaluation settings
101pub struct EvaluationSettings {145pub struct EvaluationSettings {
102 /// Limits recursion by limiting the number of stack frames146 /// Limits recursion by limiting the number of stack frames
103 pub max_stack: usize,147 pub max_stack: usize,
104 /// Limits amount of stack trace items preserved148 /// Limits amount of stack trace items preserved
105 pub max_trace: usize,149 pub max_trace: usize,
106 /// Used for s`td.extVar`
107 pub ext_vars: HashMap<IStr, TlaArg>,
108 /// Used for ext.native
109 pub ext_natives: HashMap<IStr, Cc<TraceBox<dyn Builtin>>>,
110 /// TLA vars150 /// TLA vars
111 pub tla_vars: HashMap<IStr, TlaArg>,151 pub tla_vars: HashMap<IStr, TlaArg>,
112 /// Global variables are inserted in default context152 /// Context initializer, which will be used for imports and everything
153 /// [`NoopContextInitializer`] is used by default, most likely you want to have `jrsonnet-stdlib`
113 pub globals: HashMap<IStr, Val>,154 pub context_initializer: Box<dyn ContextInitializer>,
114 /// Used to resolve file locations/contents155 /// Used to resolve file locations/contents
115 pub import_resolver: Box<dyn ImportResolver>,156 pub import_resolver: Box<dyn ImportResolver>,
116 /// Used in manifestification functions157 /// Used in manifestification functions
123 Self {164 Self {
124 max_stack: 200,165 max_stack: 200,
125 max_trace: 20,166 max_trace: 20,
126 globals: HashMap::default(),167 context_initializer: Box::new(DummyContextInitializer),
127 ext_vars: HashMap::default(),
128 ext_natives: HashMap::default(),
129 tla_vars: HashMap::default(),168 tla_vars: HashMap::default(),
130 import_resolver: Box::new(DummyImportResolver),169 import_resolver: Box::new(DummyImportResolver),
131 manifest_format: ManifestFormat::Json {170 manifest_format: ManifestFormat::Json {
151 breakpoints: Breakpoints,190 breakpoints: Breakpoints,
152191
153 /// Contains file source codes and evaluation results for imports and pretty-printed stacktraces192 /// Contains file source codes and evaluation results for imports and pretty-printed stacktraces
154 files: GcHashMap<PathBuf, FileData>,193 files: GcHashMap<SourcePath, FileData>,
155 /// Contains tla arguments and others, which aren't needed to be obtained by name
156 volatile_files: GcHashMap<String, String>,
157}194}
158struct FileData {195struct FileData {
159 string: Option<IStr>,196 string: Option<IStr>,
229pub struct State(Rc<EvaluationStateInternals>);266pub struct State(Rc<EvaluationStateInternals>);
230267
231impl State {268impl State {
269 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise
232 pub fn import_str(&self, path: PathBuf) -> Result<IStr> {270 pub fn import_resolved_str(&self, path: SourcePath) -> Result<IStr> {
233 let mut data = self.data_mut();271 let mut data = self.data_mut();
234 let mut file = data.files.raw_entry_mut().from_key(&path);272 let mut file = data.files.raw_entry_mut().from_key(&path);
235273
263 }301 }
264 Ok(file.string.as_ref().expect("just set").clone())302 Ok(file.string.as_ref().expect("just set").clone())
265 }303 }
304 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise
266 pub fn import_bin(&self, path: PathBuf) -> Result<IBytes> {305 pub fn import_resolved_bin(&self, path: SourcePath) -> Result<IBytes> {
267 let mut data = self.data_mut();306 let mut data = self.data_mut();
268 let mut file = data.files.raw_entry_mut().from_key(&path);307 let mut file = data.files.raw_entry_mut().from_key(&path);
269308
289 }328 }
290 Ok(file.bytes.as_ref().expect("just set").clone())329 Ok(file.bytes.as_ref().expect("just set").clone())
291 }330 }
331 /// Should only be called with path retrieved from [`resolve_path`], may panic otherwise
292 pub fn import(&self, path: PathBuf) -> Result<Val> {332 pub fn import_resolved(&self, path: SourcePath) -> Result<Val> {
293 let mut data = self.data_mut();333 let mut data = self.data_mut();
294 let mut file = data.files.raw_entry_mut().from_key(&path);334 let mut file = data.files.raw_entry_mut().from_key(&path);
295335
323 );363 );
324 }364 }
325 let code = file.string.as_ref().expect("just set");365 let code = file.string.as_ref().expect("just set");
326 let file_name = Source::new(path.clone()).expect("resolver should return correct name");366 let file_name = Source::new(path.clone(), code.clone());
327 if file.parsed.is_none() {367 if file.parsed.is_none() {
328 file.parsed = Some(368 file.parsed = Some(
329 jrsonnet_parser::parse(369 jrsonnet_parser::parse(
333 },373 },
334 )374 )
335 .map_err(|e| ImportSyntaxError {375 .map_err(|e| ImportSyntaxError {
336 path: file_name,376 path: file_name.clone(),
337 source_code: code.clone(),
338 error: Box::new(e),377 error: Box::new(e),
339 })?,378 })?,
340 );379 );
348 drop(data);387 drop(data);
349 let res = evaluate(self.clone(), self.create_default_context(), &parsed);388 let res = evaluate(
389 self.clone(),
390 self.create_default_context(file_name),
391 &parsed,
392 );
350393
365 }408 }
366 }409 }
367410
411 /// Has same semantics as `import 'path'` called from `from` file
368 pub fn get_source(&self, name: Source) -> Option<String> {412 pub fn import_from(&self, from: &SourcePath, path: &str) -> Result<Val> {
369 let data = self.data();413 let resolved = self.resolve_from(from, path)?;
370 match name.repr() {414 self.import_resolved(resolved)
371 Ok(real) => data
372 .files
373 .get(real)
374 .and_then(|f| f.string.as_ref())
375 .map(ToString::to_string),
376 Err(e) => data.volatile_files.get(e).map(ToOwned::to_owned),
377 }
378 }415 }
379 pub fn map_source_locations(&self, file: Source, locs: &[u32]) -> Vec<CodeLocation> {416 pub fn import(&self, path: impl AsRef<Path>) -> Result<Val> {
380 offset_to_location(&self.get_source(file).unwrap_or_else(|| "".into()), locs)
381 }
382 pub fn map_from_source_location(
383 &self,
384 file: Source,
385 line: usize,
386 column: usize,
387 ) -> Option<usize> {
388 location_to_offset(
389 &self.get_source(file).expect("file not found"),
390 line,
391 column,
392 )
393 }
394 /// Adds standard library global variable (std) to this evaluator
395 pub fn with_stdlib(&self) -> &Self {
396 let val = evaluate(417 let resolved = self.resolve(path)?;
397 self.clone(),
398 self.create_default_context(),
399 &stdlib::get_parsed_stdlib(),
400 )
401 .expect("std should not fail");
402 self.settings_mut().globals.insert("std".into(), val);418 self.import_resolved(resolved)
403 self
404 }419 }
405420
406 /// Creates context with all passed global variables421 /// Creates context with all passed global variables
407 pub fn create_default_context(&self) -> Context {422 pub fn create_default_context(&self, source: Source) -> Context {
408 let globals = &self.settings().globals;423 let context_initializer = &self.settings().context_initializer;
409 let mut new_bindings = GcHashMap::with_capacity(globals.len());
410 for (name, value) in globals.iter() {424 context_initializer.initialize(self.clone(), source)
411 new_bindings.insert(name.clone(), Thunk::evaluated(value.clone()));
412 }
413 Context::new().extend(new_bindings, None, None, None)
414 }425 }
415426
416 /// Executes code creating a new stack frame427 /// Executes code creating a new stack frame
417 pub fn push<T>(428 pub fn push<T>(
418 &self,429 &self,
419 e: CallLocation,430 e: CallLocation<'_>,
420 frame_desc: impl FnOnce() -> String,431 frame_desc: impl FnOnce() -> String,
421 f: impl FnOnce() -> Result<T>,432 f: impl FnOnce() -> Result<T>,
422 ) -> Result<T> {433 ) -> Result<T> {
545 || {556 || {
546 func.evaluate(557 func.evaluate(
547 self.clone(),558 self.clone(),
548 self.create_default_context(),559 self.create_default_context(Source::new_virtual(
560 "<tla>".into(),
561 IStr::empty(),
562 )),
549 CallLocation::native(),563 CallLocation::native(),
550 &self.settings().tla_vars,564 &self.settings().tla_vars,
551 true,565 true,
559573
560/// Internals574/// Internals
561impl State {575impl State {
562 fn data(&self) -> Ref<EvaluationData> {
563 self.0.data.borrow()
564 }
565 fn data_mut(&self) -> RefMut<EvaluationData> {576 fn data_mut(&self) -> RefMut<'_, EvaluationData> {
566 self.0.data.borrow_mut()577 self.0.data.borrow_mut()
567 }578 }
568 pub fn settings(&self) -> Ref<EvaluationSettings> {579 pub fn settings(&self) -> Ref<'_, EvaluationSettings> {
569 self.0.settings.borrow()580 self.0.settings.borrow()
570 }581 }
571 pub fn settings_mut(&self) -> RefMut<EvaluationSettings> {582 pub fn settings_mut(&self) -> RefMut<'_, EvaluationSettings> {
572 self.0.settings.borrow_mut()583 self.0.settings.borrow_mut()
573 }584 }
574}585}
575586
576/// Raw methods evaluate passed values but don't perform TLA execution587/// Raw methods evaluate passed values but don't perform TLA execution
577impl State {588impl State {
578 /// Parses and evaluates the given snippet589 /// Parses and evaluates the given snippet
579 pub fn evaluate_snippet(&self, name: String, code: String) -> Result<Val> {590 pub fn evaluate_snippet(&self, name: impl Into<IStr>, code: impl Into<IStr>) -> Result<Val> {
591 let code = code.into();
580 let source = Source::new_virtual(Cow::Owned(name.clone()));592 let source = Source::new_virtual(name.into(), code.clone());
581 let parsed = jrsonnet_parser::parse(593 let parsed = jrsonnet_parser::parse(
582 &code,594 &code,
583 &ParserSettings {595 &ParserSettings {
584 file_name: source.clone(),596 file_name: source.clone(),
585 },597 },
586 )598 )
587 .map_err(|e| ImportSyntaxError {599 .map_err(|e| ImportSyntaxError {
588 path: source,600 path: source.clone(),
589 source_code: code.clone().into(),
590 error: Box::new(e),601 error: Box::new(e),
591 })?;602 })?;
592 self.data_mut().volatile_files.insert(name, code);
593 evaluate(self.clone(), self.create_default_context(), &parsed)603 evaluate(self.clone(), self.create_default_context(source), &parsed)
594 }604 }
595}605}
596606
597/// Settings utilities607/// Settings utilities
598impl State {608impl State {
599 pub fn add_ext_var(&self, name: IStr, value: Val) {
600 self.settings_mut()
601 .ext_vars
602 .insert(name, TlaArg::Val(value));
603 }
604 pub fn add_ext_str(&self, name: IStr, value: IStr) {
605 self.settings_mut()
606 .ext_vars
607 .insert(name, TlaArg::String(value));
608 }
609 pub fn add_ext_code(&self, name: &str, code: String) -> Result<()> {
610 let source_name = format!("<extvar:{}>", name);
611 let source = Source::new_virtual(Cow::Owned(source_name.clone()));
612 let parsed = jrsonnet_parser::parse(
613 &code,
614 &ParserSettings {
615 file_name: source.clone(),
616 },
617 )
618 .map_err(|e| ImportSyntaxError {
619 path: source,
620 source_code: code.clone().into(),
621 error: Box::new(e),
622 })?;
623 self.data_mut().volatile_files.insert(source_name, code);
624 self.settings_mut()
625 .ext_vars
626 .insert(name.into(), TlaArg::Code(parsed));
627 Ok(())
628 }
629
630 pub fn add_tla(&self, name: IStr, value: Val) {609 pub fn add_tla(&self, name: IStr, value: Val) {
631 self.settings_mut()610 self.settings_mut()
638 .insert(name, TlaArg::String(value));617 .insert(name, TlaArg::String(value));
639 }618 }
640 pub fn add_tla_code(&self, name: IStr, code: &str) -> Result<()> {619 pub fn add_tla_code(&self, name: IStr, code: &str) -> Result<()> {
641 let source_name = format!("<top-level-arg:{}>", name);620 let source_name = format!("<top-level-arg:{name}>");
642 let source = Source::new_virtual(Cow::Owned(source_name.clone()));621 let source = Source::new_virtual(source_name.into(), code.into());
643 let parsed = jrsonnet_parser::parse(622 let parsed = jrsonnet_parser::parse(
644 code,623 code,
645 &ParserSettings {624 &ParserSettings {
648 )627 )
649 .map_err(|e| ImportSyntaxError {628 .map_err(|e| ImportSyntaxError {
650 path: source,629 path: source,
651 source_code: code.into(),
652 error: Box::new(e),630 error: Box::new(e),
653 })?;631 })?;
654 self.data_mut()
655 .volatile_files
656 .insert(source_name, code.to_owned());
657 self.settings_mut()632 self.settings_mut()
658 .tla_vars633 .tla_vars
659 .insert(name, TlaArg::Code(parsed));634 .insert(name, TlaArg::Code(parsed));
660 Ok(())635 Ok(())
661 }636 }
662637
638 // Only panics in case of [`ImportResolver`] contract violation
639 #[allow(clippy::missing_panics_doc)]
663 pub fn resolve_file(&self, from: &Path, path: &str) -> Result<PathBuf> {640 pub fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {
664 self.settings()641 self.import_resolver().resolve_from(from, path.as_ref())
665 .import_resolver
666 .resolve_file(from, path.as_ref())
667 }642 }
668643
644 // Only panics in case of [`ImportResolver`] contract violation
645 #[allow(clippy::missing_panics_doc)]
646 pub fn resolve(&self, path: impl AsRef<Path>) -> Result<SourcePath> {
647 self.import_resolver().resolve(path.as_ref())
648 }
669 pub fn import_resolver(&self) -> Ref<dyn ImportResolver> {649 pub fn import_resolver(&self) -> Ref<'_, dyn ImportResolver> {
670 Ref::map(self.settings(), |s| &*s.import_resolver)650 Ref::map(self.settings(), |s| &*s.import_resolver)
671 }651 }
672 pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {652 pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {
673 self.settings_mut().import_resolver = resolver;653 self.settings_mut().import_resolver = resolver;
674 }654 }
675
676 pub fn add_native(&self, name: IStr, cb: Cc<TraceBox<dyn Builtin>>) {655 pub fn context_initializer(&self) -> Ref<'_, dyn ContextInitializer> {
677 self.settings_mut().ext_natives.insert(name, cb);656 Ref::map(self.settings(), |s| &*s.context_initializer)
678 }657 }
679658
680 pub fn manifest_format(&self) -> ManifestFormat {659 pub fn manifest_format(&self) -> ManifestFormat {
684 self.settings_mut().manifest_format = format;663 self.settings_mut().manifest_format = format;
685 }664 }
686665
687 pub fn trace_format(&self) -> Ref<dyn TraceFormat> {666 pub fn trace_format(&self) -> Ref<'_, dyn TraceFormat> {
688 Ref::map(self.settings(), |s| &*s.trace_format)667 Ref::map(self.settings(), |s| &*s.trace_format)
689 }668 }
690 pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {669 pub fn set_trace_format(&self, format: Box<dyn TraceFormat>) {
modifiedcrates/jrsonnet-evaluator/src/map.rsdiffbeforeafterboth
23 }23 }
24 }24 }
25
26 pub(crate) fn new(layer: GcHashMap<IStr, Thunk<Val>>) -> Self {
27 Self(Cc::new(LayeredHashMapInternals {
28 parent: None,
29 current: layer,
30 }))
31 }
2532
26 pub fn extend(self, new_layer: GcHashMap<IStr, Thunk<Val>>) -> Self {33 pub fn extend(self, new_layer: GcHashMap<IStr, Thunk<Val>>) -> Self {
27 Self(Cc::new(LayeredHashMapInternals {34 Self(Cc::new(LayeredHashMapInternals {
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
15 function::CallLocation,15 function::CallLocation,
16 gc::{GcHashMap, GcHashSet, TraceBox},16 gc::{GcHashMap, GcHashSet, TraceBox},
17 operator::evaluate_add_op,17 operator::evaluate_add_op,
18 throw, LazyBinding, Result, State, Thunk, Unbound, Val,18 throw, MaybeUnbound, Result, State, Thunk, Unbound, Val,
19};19};
2020
21#[cfg(not(feature = "exp-preserve-order"))]21#[cfg(not(feature = "exp-preserve-order"))]
100 pub add: bool,100 pub add: bool,
101 pub visibility: Visibility,101 pub visibility: Visibility,
102 original_index: FieldIndex,102 original_index: FieldIndex,
103 pub invoke: LazyBinding,103 pub invoke: MaybeUnbound,
104 pub location: Option<ExprLocation>,104 pub location: Option<ExprLocation>,
105}105}
106106
109}109}
110110
111// Field => This111// Field => This
112type CacheKey = (IStr, WeakObjValue);
113112
114#[derive(Trace)]113#[derive(Trace)]
115enum CacheValue {114enum CacheValue {
129 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,128 assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,
130 assertions_ran: RefCell<GcHashSet<ObjValue>>,129 assertions_ran: RefCell<GcHashSet<ObjValue>>,
131 this_entries: Cc<GcHashMap<IStr, ObjMember>>,130 this_entries: Cc<GcHashMap<IStr, ObjMember>>,
132 value_cache: RefCell<GcHashMap<CacheKey, CacheValue>>,131 value_cache: RefCell<GcHashMap<IStr, CacheValue>>,
133}132}
134133
135#[derive(Clone, Trace)]134#[derive(Clone, Trace)]
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 if let Some(super_obj) = self.0.sup.as_ref() {157 if let Some(super_obj) = self.0.sup.as_ref() {
159 if f.alternate() {158 if f.alternate() {
160 write!(f, "{:#?}", super_obj)?;159 write!(f, "{super_obj:#?}")?;
161 } else {160 } else {
162 write!(f, "{:?}", super_obj)?;161 write!(f, "{super_obj:?}")?;
163 }162 }
164 write!(f, " + ")?;163 write!(f, " + ")?;
165 }164 }
209 new.insert(key, value);208 new.insert(key, value);
210 Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))209 Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))
211 }210 }
212 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder> {211 pub fn extend_field(&mut self, name: IStr) -> ObjMemberBuilder<ExtendBuilder<'_>> {
213 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())212 ObjMemberBuilder::new(ExtendBuilder(self), name, FieldIndex::default())
214 }213 }
215214
240 }239 }
241240
242 /// Run callback for every field found in object241 /// Run callback for every field found in object
242 ///
243 /// Returns true if ended prematurely
243 pub(crate) fn enum_fields(244 pub(crate) fn enum_fields(
244 &self,245 &self,
245 depth: SuperDepth,246 depth: SuperDepth,
369370
370 pub fn get(&self, s: State, key: IStr) -> Result<Option<Val>> {371 pub fn get(&self, s: State, key: IStr) -> Result<Option<Val>> {
371 self.run_assertions(s.clone())?;372 self.run_assertions(s.clone())?;
372 self.get_raw(s, key, self.0.this.clone().unwrap_or_else(|| self.clone()))373 if let Some(v) = self.0.value_cache.borrow().get(&key) {
373 }
374
375 // pub fn extend_with(self, key: )
376
377 fn get_raw(&self, s: State, key: IStr, real_this: Self) -> Result<Option<Val>> {
378 let cache_key = (key.clone(), WeakObjValue(real_this.0.downgrade()));
379
380 if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {
381 return Ok(match v {374 return Ok(match v {
382 CacheValue::Cached(v) => Some(v.clone()),375 CacheValue::Cached(v) => Some(v.clone()),
383 CacheValue::NotFound => None,376 CacheValue::NotFound => None,
388 self.0381 self.0
389 .value_cache382 .value_cache
390 .borrow_mut()383 .borrow_mut()
391 .insert(cache_key.clone(), CacheValue::Pending);384 .insert(key.clone(), CacheValue::Pending);
392 let fill_error = |e: LocError| {385 let value = self
386 .get_raw(
387 s,
388 key.clone(),
389 self.0.this.clone().unwrap_or_else(|| self.clone()),
390 )
391 .map_err(|e| {
393 self.0392 self.0
394 .value_cache393 .value_cache
395 .borrow_mut()394 .borrow_mut()
396 .insert(cache_key.clone(), CacheValue::Errored(e.clone()));395 .insert(key.clone(), CacheValue::Errored(e.clone()));
397 e396 e
398 };397 })?;
399 let value = match (self.0.this_entries.get(&key), &self.0.sup) {398 self.0.value_cache.borrow_mut().insert(
399 key,
400 value
401 .as_ref()
402 .map_or(CacheValue::NotFound, |v| CacheValue::Cached(v.clone())),
403 );
404 Ok(value)
405 }
406
407 fn get_raw(&self, s: State, key: IStr, real_this: Self) -> Result<Option<Val>> {
408 match (self.0.this_entries.get(&key), &self.0.sup) {
400 (Some(k), None) => Ok(Some(409 (Some(k), None) => Ok(Some(self.evaluate_this(s, k, real_this)?)),
401 self.evaluate_this(s, k, real_this).map_err(fill_error)?,
402 )),
403 (Some(k), Some(super_obj)) => {410 (Some(k), Some(super_obj)) => {
404 let our = self411 let our = self.evaluate_this(s.clone(), k, real_this.clone())?;
405 .evaluate_this(s.clone(), k, real_this.clone())
406 .map_err(fill_error)?;
407 if k.add {412 if k.add {
408 super_obj413 super_obj
409 .get_raw(s.clone(), key, real_this)414 .get_raw(s.clone(), key, real_this)?
410 .map_err(fill_error)?
411 .map_or(Ok(Some(our.clone())), |v| {415 .map_or(Ok(Some(our.clone())), |v| {
412 Ok(Some(evaluate_add_op(s.clone(), &v, &our)?))416 Ok(Some(evaluate_add_op(s.clone(), &v, &our)?))
413 })417 })
418 (None, Some(super_obj)) => super_obj.get_raw(s, key, real_this),422 (None, Some(super_obj)) => super_obj.get_raw(s, key, real_this),
419 (None, None) => Ok(None),423 (None, None) => Ok(None),
420 }424 }
421 .map_err(fill_error)?;
422 self.0.value_cache.borrow_mut().insert(
423 cache_key,
424 match &value {
425 Some(v) => CacheValue::Cached(v.clone()),
426 None => CacheValue::NotFound,
427 },
428 );
429 Ok(value)
430 }425 }
431 fn evaluate_this(&self, s: State, v: &ObjMember, real_this: Self) -> Result<Val> {426 fn evaluate_this(&self, s: State, v: &ObjMember, real_this: Self) -> Result<Val> {
432 v.invoke427 v.invoke
507 self.assertions.push(assertion);502 self.assertions.push(assertion);
508 self503 self
509 }504 }
510 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder> {505 pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {
511 let field_index = self.next_field_index;506 let field_index = self.next_field_index;
512 self.next_field_index = self.next_field_index.next();507 self.next_field_index = self.next_field_index.next();
513 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)508 ObjMemberBuilder::new(ValueBuilder(self), name, field_index)
565 self.location = Some(location);560 self.location = Some(location);
566 self561 self
567 }562 }
568 fn build_member(self, binding: LazyBinding) -> (Kind, IStr, ObjMember) {563 fn build_member(self, binding: MaybeUnbound) -> (Kind, IStr, ObjMember) {
569 (564 (
570 self.kind,565 self.kind,
571 self.name,566 self.name,
581}576}
582577
583pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);578pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);
584impl<'v> ObjMemberBuilder<ValueBuilder<'v>> {579impl ObjMemberBuilder<ValueBuilder<'_>> {
585 pub fn value(self, s: State, value: Val) -> Result<()> {580 pub fn value(self, s: State, value: Val) -> Result<()> {
586 self.binding(s, LazyBinding::Bound(Thunk::evaluated(value)))581 self.binding(s, MaybeUnbound::Bound(Thunk::evaluated(value)))
587 }582 }
588 pub fn bindable(583 pub fn bindable(
589 self,584 self,
590 s: State,585 s: State,
591 bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>,586 bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>,
592 ) -> Result<()> {587 ) -> Result<()> {
593 self.binding(s, LazyBinding::Bindable(Cc::new(bindable)))588 self.binding(s, MaybeUnbound::Unbound(Cc::new(bindable)))
594 }589 }
595 pub fn binding(self, s: State, binding: LazyBinding) -> Result<()> {590 pub fn binding(self, s: State, binding: MaybeUnbound) -> Result<()> {
596 let (receiver, name, member) = self.build_member(binding);591 let (receiver, name, member) = self.build_member(binding);
597 let location = member.location.clone();592 let location = member.location.clone();
598 let old = receiver.0.map.insert(name.clone(), member);593 let old = receiver.0.map.insert(name.clone(), member);
608}603}
609604
610pub struct ExtendBuilder<'v>(&'v mut ObjValue);605pub struct ExtendBuilder<'v>(&'v mut ObjValue);
611impl<'v> ObjMemberBuilder<ExtendBuilder<'v>> {606impl ObjMemberBuilder<ExtendBuilder<'_>> {
612 pub fn value(self, value: Val) {607 pub fn value(self, value: Val) {
613 self.binding(LazyBinding::Bound(Thunk::evaluated(value)));608 self.binding(MaybeUnbound::Bound(Thunk::evaluated(value)));
614 }609 }
615 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>) {610 pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Thunk<Val>>>) {
616 self.binding(LazyBinding::Bindable(Cc::new(bindable)));611 self.binding(MaybeUnbound::Unbound(Cc::new(bindable)));
617 }612 }
618 pub fn binding(self, binding: LazyBinding) {613 pub fn binding(self, binding: MaybeUnbound) {
619 let (receiver, name, member) = self.build_member(binding);614 let (receiver, name, member) = self.build_member(binding);
620 let new = receiver.0.clone();615 let new = receiver.0.clone();
621 *receiver.0 = new.extend_with_raw_member(name, member);616 *receiver.0 = new.extend_with_raw_member(name, member);
deletedcrates/jrsonnet-evaluator/src/stdlib/expr.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-evaluator/src/stdlib/format.rsdiffbeforeafterboth
3636
37type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;37type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>;
3838
39pub fn try_parse_mapping_key(str: &str) -> ParseResult<&str> {39pub fn try_parse_mapping_key(str: &str) -> ParseResult<'_, &str> {
40 if str.is_empty() {40 if str.is_empty() {
41 return Err(TruncatedFormatCode);41 return Err(TruncatedFormatCode);
42 }42 }
45 let mut i = 1;45 let mut i = 1;
46 while i < bytes.len() {46 while i < bytes.len() {
47 if bytes[i] == b')' {47 if bytes[i] == b')' {
48 return Ok((&str[1..i as usize], &str[i as usize + 1..]));48 return Ok((&str[1..i], &str[i + 1..]));
49 }49 }
50 i += 1;50 i += 1;
51 }51 }
96 pub sign: bool,96 pub sign: bool,
97}97}
9898
99pub fn try_parse_cflags(str: &str) -> ParseResult<CFlags> {99pub fn try_parse_cflags(str: &str) -> ParseResult<'_, CFlags> {
100 if str.is_empty() {100 if str.is_empty() {
101 return Err(TruncatedFormatCode);101 return Err(TruncatedFormatCode);
102 }102 }
125 Star,125 Star,
126 Fixed(usize),126 Fixed(usize),
127}127}
128pub fn try_parse_field_width(str: &str) -> ParseResult<Width> {128pub fn try_parse_field_width(str: &str) -> ParseResult<'_, Width> {
129 if str.is_empty() {129 if str.is_empty() {
130 return Err(TruncatedFormatCode);130 return Err(TruncatedFormatCode);
131 }131 }
146 Ok((Width::Fixed(out), &str[digits..]))146 Ok((Width::Fixed(out), &str[digits..]))
147}147}
148148
149pub fn try_parse_precision(str: &str) -> ParseResult<Option<Width>> {149pub fn try_parse_precision(str: &str) -> ParseResult<'_, Option<Width>> {
150 if str.is_empty() {150 if str.is_empty() {
151 return Err(TruncatedFormatCode);151 return Err(TruncatedFormatCode);
152 }152 }
159}159}
160160
161// Only skips161// Only skips
162pub fn try_parse_length_modifier(str: &str) -> ParseResult<()> {162pub fn try_parse_length_modifier(str: &str) -> ParseResult<'_, ()> {
163 if str.is_empty() {163 if str.is_empty() {
164 return Err(TruncatedFormatCode);164 return Err(TruncatedFormatCode);
165 }165 }
191 caps: bool,191 caps: bool,
192}192}
193193
194pub fn parse_conversion_type(str: &str) -> ParseResult<ConvType> {194pub fn parse_conversion_type(str: &str) -> ParseResult<'_, ConvType> {
195 if str.is_empty() {195 if str.is_empty() {
196 return Err(TruncatedFormatCode);196 return Err(TruncatedFormatCode);
197 }197 }
226 convtype: ConvTypeV,226 convtype: ConvTypeV,
227 caps: bool,227 caps: bool,
228}228}
229pub fn parse_code(str: &str) -> ParseResult<Code> {229pub fn parse_code(str: &str) -> ParseResult<'_, Code<'_>> {
230 if str.is_empty() {230 if str.is_empty() {
231 return Err(TruncatedFormatCode);231 return Err(TruncatedFormatCode);
232 }232 }
255 String(&'s str),255 String(&'s str),
256 Code(Code<'s>),256 Code(Code<'s>),
257}257}
258pub fn parse_codes(mut str: &str) -> Result<Vec<Element>> {258pub fn parse_codes(mut str: &str) -> Result<Vec<Element<'_>>> {
259 let mut bytes = str.as_bytes();259 let mut bytes = str.as_bytes();
260 let mut out = vec![];260 let mut out = vec![];
261 let mut offset = 0;261 let mut offset = 0;
285#[inline]285#[inline]
286pub fn render_integer(286pub fn render_integer(
287 out: &mut String,287 out: &mut String,
288 iv: i64,288 iv: f64,
289 padding: usize,289 padding: usize,
290 precision: usize,290 precision: usize,
291 blank: bool,291 blank: bool,
294 prefix: &str,294 prefix: &str,
295 caps: bool,295 caps: bool,
296) {296) {
297 let radix = radix as f64;
298 let iv = iv.floor();
297 // Digit char indexes in reverse order, i.e299 // Digit char indexes in reverse order, i.e
298 // for radix = 16 and n = 12f: [15, 2, 1]300 // for radix = 16 and n = 12f: [15, 2, 1]
299 let digits = if iv == 0 {301 let digits = if iv == 0.0 {
300 vec![0u8]302 vec![0u8]
301 } else {303 } else {
302 let mut v = iv.abs();304 let mut v = iv.abs();
303 let mut nums = Vec::with_capacity(1);305 let mut nums = Vec::with_capacity(1);
304 while v > 0 {306 while v != 0.0 {
305 nums.push((v % radix) as u8);307 nums.push((v % radix) as u8);
306 v /= radix;308 v = (v / radix).floor();
307 }309 }
308 nums310 nums
309 };311 };
310 let neg = iv < 0;312 let neg = iv < 0.0;
313 #[allow(clippy::bool_to_int_with_if)]
311 let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });314 let zp = padding.saturating_sub(if neg || blank || sign { 1 } else { 0 });
312 let zp2 = zp315 let zp2 = zp
313 .max(precision)316 .max(precision)
335338
336pub fn render_decimal(339pub fn render_decimal(
337 out: &mut String,340 out: &mut String,
338 iv: i64,341 iv: f64,
339 padding: usize,342 padding: usize,
340 precision: usize,343 precision: usize,
341 blank: bool,344 blank: bool,
345}348}
346pub fn render_octal(349pub fn render_octal(
347 out: &mut String,350 out: &mut String,
348 iv: i64,351 iv: f64,
349 padding: usize,352 padding: usize,
350 precision: usize,353 precision: usize,
351 alt: bool,354 alt: bool,
360 blank,363 blank,
361 sign,364 sign,
362 8,365 8,
363 if alt && iv != 0 { "0" } else { "" },366 if alt && iv != 0.0 { "0" } else { "" },
364 false,367 false,
365 );368 );
366}369}
367370
368#[allow(clippy::fn_params_excessive_bools)]371#[allow(clippy::fn_params_excessive_bools)]
369pub fn render_hexadecimal(372pub fn render_hexadecimal(
370 out: &mut String,373 out: &mut String,
371 iv: i64,374 iv: f64,
372 padding: usize,375 padding: usize,
373 precision: usize,376 precision: usize,
374 alt: bool,377 alt: bool,
404 ensure_pt: bool,407 ensure_pt: bool,
405 trailing: bool,408 trailing: bool,
406) {409) {
410 #[allow(clippy::bool_to_int_with_if)]
407 let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };411 let dot_size = if precision == 0 && !ensure_pt { 0 } else { 1 };
408 padding = padding.saturating_sub(dot_size + precision);412 padding = padding.saturating_sub(dot_size + precision);
409 render_decimal(out, n.floor() as i64, padding, 0, blank, sign);413 render_decimal(out, n.floor(), padding, 0, blank, sign);
410 if precision == 0 {414 if precision == 0 {
411 if ensure_pt {415 if ensure_pt {
412 out.push('.');416 out.push('.');
420 if trailing || frac > 0.0 {424 if trailing || frac > 0.0 {
421 out.push('.');425 out.push('.');
422 let mut frac_str = String::new();426 let mut frac_str = String::new();
423 render_decimal(&mut frac_str, frac as i64, precision, 0, false, false);427 render_decimal(&mut frac_str, frac, precision, 0, false, false);
424 let mut trim = frac_str.len();428 let mut trim = frac_str.len();
425 if !trailing {429 if !trailing {
426 for b in frac_str.as_bytes().iter().rev() {430 for b in frac_str.as_bytes().iter().rev() {
454 n / 10.0_f64.powf(exponent)458 n / 10.0_f64.powf(exponent)
455 };459 };
456 let mut exponent_str = String::new();460 let mut exponent_str = String::new();
457 render_decimal(&mut exponent_str, exponent as i64, 3, 0, false, true);461 render_decimal(&mut exponent_str, exponent, 3, 0, false, true);
458462
459 // +1 for e463 // +1 for e
460 padding = padding.saturating_sub(exponent_str.len() + 1);464 padding = padding.saturating_sub(exponent_str.len() + 1);
471 s: State,475 s: State,
472 out: &mut String,476 out: &mut String,
473 value: &Val,477 value: &Val,
474 code: &Code,478 code: &Code<'_>,
475 width: usize,479 width: usize,
476 precision: Option<usize>,480 precision: Option<usize>,
477) -> Result<()> {481) -> Result<()> {
478 let clfags = &code.cflags;482 let clfags = &code.cflags;
479 let (fpprec, iprec) = match precision {483 let (fpprec, iprec) = precision.map_or((6, 0), |v| (v, v));
480 Some(v) => (v, v),
481 None => (6, 0),
482 };
483 let padding = if clfags.zero && !clfags.left {484 let padding = if clfags.zero && !clfags.left {
484 width485 width
485 } else {486 } else {
495 let value = f64::from_untyped(value.clone(), s)?;496 let value = f64::from_untyped(value.clone(), s)?;
496 render_decimal(497 render_decimal(
497 &mut tmp_out,498 &mut tmp_out,
498 value as i64,499 value,
499 padding,500 padding,
500 iprec,501 iprec,
501 clfags.blank,502 clfags.blank,
506 let value = f64::from_untyped(value.clone(), s)?;507 let value = f64::from_untyped(value.clone(), s)?;
507 render_octal(508 render_octal(
508 &mut tmp_out,509 &mut tmp_out,
509 value as i64,510 value,
510 padding,511 padding,
511 iprec,512 iprec,
512 clfags.alt,513 clfags.alt,
518 let value = f64::from_untyped(value.clone(), s)?;519 let value = f64::from_untyped(value.clone(), s)?;
519 render_hexadecimal(520 render_hexadecimal(
520 &mut tmp_out,521 &mut tmp_out,
521 value as i64,522 value,
522 padding,523 padding,
523 iprec,524 iprec,
524 clfags.alt,525 clfags.alt,
585 }586 }
586 ConvTypeV::Char => match value.clone() {587 ConvTypeV::Char => match value.clone() {
587 Val::Num(n) => tmp_out588 Val::Num(n) => tmp_out.push(
588 .push(std::char::from_u32(n as u32).ok_or(InvalidUnicodeCodepointGot(n as u32))?),589 std::char::from_u32(n as u32)
590 .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,
591 ),
589 Val::Str(s) => {592 Val::Str(s) => {
590 if s.chars().count() != 1 {593 if s.chars().count() != 1 {
775 "+10 "778 "+10 "
776 );779 );
777 assert_eq!(780 assert_eq!(format_arr(s, "%+-04o", &[Val::Num(8.0)]).unwrap(), "+10 ");
778 format_arr(s.clone(), "%+-04o", &[Val::Num(8.0)]).unwrap(),
779 "+10 "
780 );
781 }781 }
modifiedcrates/jrsonnet-evaluator/src/stdlib/manifest.rsdiffbeforeafterboth
49 }49 }
50 Val::Null => buf.push_str("null"),50 Val::Null => buf.push_str("null"),
51 Val::Str(s) => escape_string_json_buf(s, buf),51 Val::Str(s) => escape_string_json_buf(s, buf),
52 Val::Num(n) => write!(buf, "{}", n).unwrap(),52 Val::Num(n) => write!(buf, "{n}").unwrap(),
53 Val::Arr(items) => {53 Val::Arr(items) => {
54 buf.push('[');54 buf.push('[');
55 if !items.is_empty() {55 if !items.is_empty() {
116 || {116 || {
117 let value = obj.get(s.clone(), field.clone())?.unwrap();117 let value = obj.get(s.clone(), field.clone())?.unwrap();
118 manifest_json_ex_buf(s.clone(), &value, buf, cur_padding, options)?;118 manifest_json_ex_buf(s.clone(), &value, buf, cur_padding, options)?;
119 Ok(Val::Null)119 Ok(())
120 },120 },
121 )?;121 )?;
122 }122 }
modifiedcrates/jrsonnet-evaluator/src/stdlib/mod.rsdiffbeforeafterboth
1// All builtins should return results1// All builtins should return results
2#![allow(clippy::unnecessary_wraps)]2#![allow(clippy::unnecessary_wraps)]
3
4use std::collections::HashMap;
53
6use format::{format_arr, format_obj};4use format::{format_arr, format_obj};
7use jrsonnet_gcmodule::Cc;
8use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_interner::IStr;
9use serde::Deserialize;
10use serde_yaml_with_quirks::DeserializingQuirks;
116
12use crate::{7use crate::{error::Result, function::CallLocation, State, Val};
13 error::{Error::*, Result},
14 function::{builtin::StaticBuiltin, ArgLike, CallLocation, FuncVal},
15 operator::evaluate_mod_op,
16 stdlib::manifest::{manifest_yaml_ex, ManifestYamlOptions},
17 throw,
18 typed::{Any, BoundedUsize, Either2, Either4, PositiveF64, Typed, VecVal, M1},
19 val::{equals, primitive_equals, ArrValue, IndexableVal, Slice},
20 Either, ObjValue, State, Val,
21};
22
23pub mod expr;
24pub use expr::*;
25
26use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};
278
28pub mod format;9pub mod format;
29pub mod manifest;10pub mod manifest;
30pub mod sort;
3111
32pub fn std_format(s: State, str: IStr, vals: Val) -> Result<String> {12pub fn std_format(s: State, str: IStr, vals: Val) -> Result<String> {
33 s.push(13 s.push(
34 CallLocation::native(),14 CallLocation::native(),
35 || format!("std.format of {}", str),15 || format!("std.format of {str}"),
36 || {16 || {
37 Ok(match vals {17 Ok(match vals {
38 Val::Arr(vals) => format_arr(s.clone(), &str, &vals.evaluated(s.clone())?)?,18 Val::Arr(vals) => format_arr(s.clone(), &str, &vals.evaluated(s.clone())?)?,
43 )23 )
44}24}
45
46pub fn std_slice(
47 indexable: IndexableVal,
48 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
49 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
50 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
51) -> Result<Val> {
52 match &indexable {
53 IndexableVal::Str(s) => {
54 let index = index.as_deref().copied().unwrap_or(0);
55 let end = end.as_deref().copied().unwrap_or(usize::MAX);
56 let step = step.as_deref().copied().unwrap_or(1);
57
58 if index >= end {
59 return Ok(Val::Str("".into()));
60 }
61
62 Ok(Val::Str(
63 (s.chars()
64 .skip(index)
65 .take(end - index)
66 .step_by(step)
67 .collect::<String>())
68 .into(),
69 ))
70 }
71 IndexableVal::Arr(arr) => {
72 let index = index.as_deref().copied().unwrap_or(0);
73 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());
74 let step = step.as_deref().copied().unwrap_or(1);
75
76 if index >= end {
77 return Ok(Val::Arr(ArrValue::new_eager()));
78 }
79
80 Ok(Val::Arr(ArrValue::Slice(Box::new(Slice {
81 inner: arr.clone(),
82 from: index as u32,
83 to: end as u32,
84 step: step as u32,
85 }))))
86 }
87 }
88}
89
90type BuiltinsType = HashMap<IStr, &'static dyn StaticBuiltin>;
91
92thread_local! {
93 pub static BUILTINS: BuiltinsType = {
94 [
95 ("length".into(), builtin_length::INST),
96 ("type".into(), builtin_type::INST),
97 ("makeArray".into(), builtin_make_array::INST),
98 ("codepoint".into(), builtin_codepoint::INST),
99 ("objectFieldsEx".into(), builtin_object_fields_ex::INST),
100 ("objectHasEx".into(), builtin_object_has_ex::INST),
101 ("slice".into(), builtin_slice::INST),
102 ("substr".into(), builtin_substr::INST),
103 ("primitiveEquals".into(), builtin_primitive_equals::INST),
104 ("equals".into(), builtin_equals::INST),
105 ("modulo".into(), builtin_modulo::INST),
106 ("mod".into(), builtin_mod::INST),
107 ("floor".into(), builtin_floor::INST),
108 ("ceil".into(), builtin_ceil::INST),
109 ("log".into(), builtin_log::INST),
110 ("pow".into(), builtin_pow::INST),
111 ("sqrt".into(), builtin_sqrt::INST),
112 ("sin".into(), builtin_sin::INST),
113 ("cos".into(), builtin_cos::INST),
114 ("tan".into(), builtin_tan::INST),
115 ("asin".into(), builtin_asin::INST),
116 ("acos".into(), builtin_acos::INST),
117 ("atan".into(), builtin_atan::INST),
118 ("exp".into(), builtin_exp::INST),
119 ("mantissa".into(), builtin_mantissa::INST),
120 ("exponent".into(), builtin_exponent::INST),
121 ("extVar".into(), builtin_ext_var::INST),
122 ("native".into(), builtin_native::INST),
123 ("filter".into(), builtin_filter::INST),
124 ("map".into(), builtin_map::INST),
125 ("flatMap".into(), builtin_flatmap::INST),
126 ("foldl".into(), builtin_foldl::INST),
127 ("foldr".into(), builtin_foldr::INST),
128 ("sort".into(), builtin_sort::INST),
129 ("format".into(), builtin_format::INST),
130 ("range".into(), builtin_range::INST),
131 ("char".into(), builtin_char::INST),
132 ("encodeUTF8".into(), builtin_encode_utf8::INST),
133 ("decodeUTF8".into(), builtin_decode_utf8::INST),
134 ("md5".into(), builtin_md5::INST),
135 ("base64".into(), builtin_base64::INST),
136 ("base64DecodeBytes".into(), builtin_base64_decode_bytes::INST),
137 ("base64Decode".into(), builtin_base64_decode::INST),
138 ("trace".into(), builtin_trace::INST),
139 ("join".into(), builtin_join::INST),
140 ("escapeStringJson".into(), builtin_escape_string_json::INST),
141 ("manifestJsonEx".into(), builtin_manifest_json_ex::INST),
142 ("manifestYamlDoc".into(), builtin_manifest_yaml_doc::INST),
143 ("reverse".into(), builtin_reverse::INST),
144 ("strReplace".into(), builtin_str_replace::INST),
145 ("splitLimit".into(), builtin_splitlimit::INST),
146 ("parseJson".into(), builtin_parse_json::INST),
147 ("parseYaml".into(), builtin_parse_yaml::INST),
148 ("asciiUpper".into(), builtin_ascii_upper::INST),
149 ("asciiLower".into(), builtin_ascii_lower::INST),
150 ("member".into(), builtin_member::INST),
151 ("count".into(), builtin_count::INST),
152 ("any".into(), builtin_any::INST),
153 ("all".into(), builtin_all::INST),
154 ].iter().cloned().collect()
155 };
156}
157
158#[jrsonnet_macros::builtin]
159fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> Result<usize> {
160 use Either4::*;
161 Ok(match x {
162 A(x) => x.chars().count(),
163 B(x) => x.len(),
164 C(x) => x.len(),
165 D(f) => f.params_len(),
166 })
167}
168
169#[jrsonnet_macros::builtin]
170fn builtin_type(x: Any) -> Result<IStr> {
171 Ok(x.0.value_type().name().into())
172}
173
174#[jrsonnet_macros::builtin]
175fn builtin_make_array(s: State, sz: usize, func: FuncVal) -> Result<VecVal> {
176 let mut out = Vec::with_capacity(sz);
177 for i in 0..sz {
178 out.push(func.evaluate_simple(s.clone(), &(i as f64,))?);
179 }
180 Ok(VecVal(Cc::new(out)))
181}
182
183#[jrsonnet_macros::builtin]
184const fn builtin_codepoint(str: char) -> Result<u32> {
185 Ok(str as u32)
186}
187
188#[jrsonnet_macros::builtin]
189fn builtin_object_fields_ex(
190 obj: ObjValue,
191 inc_hidden: bool,
192 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
193) -> Result<VecVal> {
194 #[cfg(feature = "exp-preserve-order")]
195 let preserve_order = preserve_order.unwrap_or(false);
196 let out = obj.fields_ex(
197 inc_hidden,
198 #[cfg(feature = "exp-preserve-order")]
199 preserve_order,
200 );
201 Ok(VecVal(Cc::new(
202 out.into_iter().map(Val::Str).collect::<Vec<_>>(),
203 )))
204}
205
206#[jrsonnet_macros::builtin]
207fn builtin_object_has_ex(obj: ObjValue, f: IStr, inc_hidden: bool) -> Result<bool> {
208 Ok(obj.has_field_ex(f, inc_hidden))
209}
210
211#[jrsonnet_macros::builtin]
212fn builtin_parse_json(st: State, s: IStr) -> Result<Any> {
213 use serde_json::Value;
214 let value: Value = serde_json::from_str(&s)
215 .map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;
216 Ok(Any(Value::into_untyped(value, st)?))
217}
218
219#[jrsonnet_macros::builtin]
220fn builtin_parse_yaml(st: State, s: IStr) -> Result<Any> {
221 use serde_json::Value;
222 let value = serde_yaml_with_quirks::Deserializer::from_str_with_quirks(
223 &s,
224 DeserializingQuirks { old_octals: true },
225 );
226 let mut out = vec![];
227 for item in value {
228 let value = Value::deserialize(item)
229 .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;
230 let val = Value::into_untyped(value, st.clone())?;
231 out.push(val);
232 }
233 Ok(Any(if out.is_empty() {
234 Val::Null
235 } else if out.len() == 1 {
236 out.into_iter().next().unwrap()
237 } else {
238 Val::Arr(out.into())
239 }))
240}
241
242#[jrsonnet_macros::builtin]
243fn builtin_slice(
244 indexable: IndexableVal,
245 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
246 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
247 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
248) -> Result<Any> {
249 std_slice(indexable, index, end, step).map(Any)
250}
251
252#[jrsonnet_macros::builtin]
253fn builtin_substr(str: IStr, from: usize, len: usize) -> Result<String> {
254 Ok(str.chars().skip(from as usize).take(len as usize).collect())
255}
256
257#[jrsonnet_macros::builtin]
258fn builtin_primitive_equals(a: Any, b: Any) -> Result<bool> {
259 primitive_equals(&a.0, &b.0)
260}
261
262#[jrsonnet_macros::builtin]
263fn builtin_equals(s: State, a: Any, b: Any) -> Result<bool> {
264 equals(s, &a.0, &b.0)
265}
266
267#[jrsonnet_macros::builtin]
268fn builtin_modulo(a: f64, b: f64) -> Result<f64> {
269 Ok(a % b)
270}
271
272#[jrsonnet_macros::builtin]
273fn builtin_mod(s: State, a: Either![f64, IStr], b: Any) -> Result<Any> {
274 use Either2::*;
275 Ok(Any(evaluate_mod_op(
276 s,
277 &match a {
278 A(v) => Val::Num(v),
279 B(s) => Val::Str(s),
280 },
281 &b.0,
282 )?))
283}
284
285#[jrsonnet_macros::builtin]
286fn builtin_floor(x: f64) -> Result<f64> {
287 Ok(x.floor())
288}
289
290#[jrsonnet_macros::builtin]
291fn builtin_ceil(x: f64) -> Result<f64> {
292 Ok(x.ceil())
293}
294
295#[jrsonnet_macros::builtin]
296fn builtin_log(n: f64) -> Result<f64> {
297 Ok(n.ln())
298}
299
300#[jrsonnet_macros::builtin]
301fn builtin_pow(x: f64, n: f64) -> Result<f64> {
302 Ok(x.powf(n))
303}
304
305#[jrsonnet_macros::builtin]
306fn builtin_sqrt(x: PositiveF64) -> Result<f64> {
307 Ok(x.0.sqrt())
308}
309
310#[jrsonnet_macros::builtin]
311fn builtin_sin(x: f64) -> Result<f64> {
312 Ok(x.sin())
313}
314
315#[jrsonnet_macros::builtin]
316fn builtin_cos(x: f64) -> Result<f64> {
317 Ok(x.cos())
318}
319
320#[jrsonnet_macros::builtin]
321fn builtin_tan(x: f64) -> Result<f64> {
322 Ok(x.tan())
323}
324
325#[jrsonnet_macros::builtin]
326fn builtin_asin(x: f64) -> Result<f64> {
327 Ok(x.asin())
328}
329
330#[jrsonnet_macros::builtin]
331fn builtin_acos(x: f64) -> Result<f64> {
332 Ok(x.acos())
333}
334
335#[jrsonnet_macros::builtin]
336fn builtin_atan(x: f64) -> Result<f64> {
337 Ok(x.atan())
338}
339
340#[jrsonnet_macros::builtin]
341fn builtin_exp(x: f64) -> Result<f64> {
342 Ok(x.exp())
343}
344
345fn frexp(s: f64) -> (f64, i16) {
346 if 0.0 == s {
347 (s, 0)
348 } else {
349 let lg = s.abs().log2();
350 let x = (lg - lg.floor() - 1.0).exp2();
351 let exp = lg.floor() + 1.0;
352 (s.signum() * x, exp as i16)
353 }
354}
355
356#[jrsonnet_macros::builtin]
357fn builtin_mantissa(x: f64) -> Result<f64> {
358 Ok(frexp(x).0)
359}
360
361#[jrsonnet_macros::builtin]
362fn builtin_exponent(x: f64) -> Result<i16> {
363 Ok(frexp(x).1)
364}
365
366#[jrsonnet_macros::builtin]
367fn builtin_ext_var(s: State, x: IStr) -> Result<Any> {
368 let ctx = s.create_default_context();
369 Ok(Any(s
370 .clone()
371 .settings()
372 .ext_vars
373 .get(&x)
374 .cloned()
375 .ok_or(UndefinedExternalVariable(x))?
376 .evaluate_arg(s.clone(), ctx, true)?
377 .evaluate(s)?))
378}
379
380#[jrsonnet_macros::builtin]
381fn builtin_native(s: State, name: IStr) -> Result<Any> {
382 Ok(Any(s
383 .settings()
384 .ext_natives
385 .get(&name)
386 .cloned()
387 .map_or(Val::Null, |v| {
388 Val::Func(FuncVal::Builtin(v.clone()))
389 })))
390}
391
392#[jrsonnet_macros::builtin]
393fn builtin_filter(s: State, func: FuncVal, arr: ArrValue) -> Result<ArrValue> {
394 arr.filter(s.clone(), |val| {
395 bool::from_untyped(
396 func.evaluate_simple(s.clone(), &(Any(val.clone()),))?,
397 s.clone(),
398 )
399 })
400}
401
402#[jrsonnet_macros::builtin]
403fn builtin_map(s: State, func: FuncVal, arr: ArrValue) -> Result<ArrValue> {
404 arr.map(s.clone(), |val| {
405 func.evaluate_simple(s.clone(), &(Any(val),))
406 })
407}
408
409#[jrsonnet_macros::builtin]
410fn builtin_flatmap(s: State, func: FuncVal, arr: IndexableVal) -> Result<IndexableVal> {
411 match arr {
412 IndexableVal::Str(str) => {
413 let mut out = String::new();
414 for c in str.chars() {
415 match func.evaluate_simple(s.clone(), &(c.to_string(),))? {
416 Val::Str(o) => out.push_str(&o),
417 Val::Null => continue,
418 _ => throw!(RuntimeError(
419 "in std.join all items should be strings".into()
420 )),
421 };
422 }
423 Ok(IndexableVal::Str(out.into()))
424 }
425 IndexableVal::Arr(a) => {
426 let mut out = Vec::new();
427 for el in a.iter(s.clone()) {
428 let el = el?;
429 match func.evaluate_simple(s.clone(), &(Any(el),))? {
430 Val::Arr(o) => {
431 for oe in o.iter(s.clone()) {
432 out.push(oe?);
433 }
434 }
435 Val::Null => continue,
436 _ => throw!(RuntimeError(
437 "in std.join all items should be arrays".into()
438 )),
439 };
440 }
441 Ok(IndexableVal::Arr(out.into()))
442 }
443 }
444}
445
446#[jrsonnet_macros::builtin]
447fn builtin_foldl(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
448 let mut acc = init.0;
449 for i in arr.iter(s.clone()) {
450 acc = func.evaluate_simple(s.clone(), &(Any(acc), Any(i?)))?;
451 }
452 Ok(Any(acc))
453}
454
455#[jrsonnet_macros::builtin]
456fn builtin_foldr(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
457 let mut acc = init.0;
458 for i in arr.iter(s.clone()).rev() {
459 acc = func.evaluate_simple(s.clone(), &(Any(i?), Any(acc)))?;
460 }
461 Ok(Any(acc))
462}
463
464#[jrsonnet_macros::builtin]
465#[allow(non_snake_case)]
466fn builtin_sort(s: State, arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
467 if arr.len() <= 1 {
468 return Ok(arr);
469 }
470 Ok(ArrValue::Eager(sort::sort(
471 s.clone(),
472 arr.evaluated(s)?,
473 keyF.unwrap_or_else(FuncVal::identity),
474 )?))
475}
476
477#[jrsonnet_macros::builtin]
478fn builtin_format(s: State, str: IStr, vals: Any) -> Result<String> {
479 std_format(s, str, vals.0)
480}
481
482#[jrsonnet_macros::builtin]
483fn builtin_range(from: i32, to: i32) -> Result<ArrValue> {
484 if to < from {
485 return Ok(ArrValue::new_eager());
486 }
487 Ok(ArrValue::new_range(from, to))
488}
489
490#[jrsonnet_macros::builtin]
491fn builtin_char(n: u32) -> Result<char> {
492 Ok(std::char::from_u32(n as u32).ok_or(InvalidUnicodeCodepointGot(n as u32))?)
493}
494
495#[jrsonnet_macros::builtin]
496fn builtin_encode_utf8(str: IStr) -> Result<IBytes> {
497 Ok(str.cast_bytes())
498}
499
500#[jrsonnet_macros::builtin]
501fn builtin_decode_utf8(arr: IBytes) -> Result<IStr> {
502 Ok(arr
503 .cast_str()
504 .ok_or_else(|| RuntimeError("bad utf8".into()))?)
505}
506
507#[jrsonnet_macros::builtin]
508fn builtin_md5(str: IStr) -> Result<String> {
509 Ok(format!("{:x}", md5::compute(&str.as_bytes())))
510}
511
512#[jrsonnet_macros::builtin]
513fn builtin_trace(s: State, loc: CallLocation, str: IStr, rest: Any) -> Result<Any> {
514 eprint!("TRACE:");
515 if let Some(loc) = loc.0 {
516 let locs = s.map_source_locations(loc.0.clone(), &[loc.1]);
517 eprint!(" {}:{}", loc.0.short_display(), locs[0].line);
518 }
519 eprintln!(" {}", str);
520 Ok(rest) as Result<Any>
521}
522
523#[jrsonnet_macros::builtin]
524fn builtin_base64(input: Either![IBytes, IStr]) -> Result<String> {
525 use Either2::*;
526 Ok(match input {
527 A(a) => base64::encode(a.as_slice()),
528 B(l) => base64::encode(l.bytes().collect::<Vec<_>>()),
529 })
530}
531
532#[jrsonnet_macros::builtin]
533fn builtin_base64_decode_bytes(input: IStr) -> Result<IBytes> {
534 Ok(base64::decode(&input.as_bytes())
535 .map_err(|_| RuntimeError("bad base64".into()))?
536 .as_slice()
537 .into())
538}
539
540#[jrsonnet_macros::builtin]
541fn builtin_base64_decode(input: IStr) -> Result<String> {
542 let bytes = base64::decode(&input.as_bytes()).map_err(|_| RuntimeError("bad base64".into()))?;
543 Ok(String::from_utf8(bytes).map_err(|_| RuntimeError("bad utf8".into()))?)
544}
545
546#[jrsonnet_macros::builtin]
547fn builtin_join(s: State, sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {
548 Ok(match sep {
549 IndexableVal::Arr(joiner_items) => {
550 let mut out = Vec::new();
551
552 let mut first = true;
553 for item in arr.iter(s.clone()) {
554 let item = item?.clone();
555 if let Val::Arr(items) = item {
556 if !first {
557 out.reserve(joiner_items.len());
558 // TODO: extend
559 for item in joiner_items.iter(s.clone()) {
560 out.push(item?);
561 }
562 }
563 first = false;
564 out.reserve(items.len());
565 for item in items.iter(s.clone()) {
566 out.push(item?);
567 }
568 } else if matches!(item, Val::Null) {
569 continue;
570 } else {
571 throw!(RuntimeError(
572 "in std.join all items should be arrays".into()
573 ));
574 }
575 }
576
577 IndexableVal::Arr(out.into())
578 }
579 IndexableVal::Str(sep) => {
580 let mut out = String::new();
581
582 let mut first = true;
583 for item in arr.iter(s) {
584 let item = item?.clone();
585 if let Val::Str(item) = item {
586 if !first {
587 out += &sep;
588 }
589 first = false;
590 out += &item;
591 } else if matches!(item, Val::Null) {
592 continue;
593 } else {
594 throw!(RuntimeError(
595 "in std.join all items should be strings".into()
596 ));
597 }
598 }
599
600 IndexableVal::Str(out.into())
601 }
602 })
603}
604
605#[jrsonnet_macros::builtin]
606fn builtin_escape_string_json(str_: IStr) -> Result<String> {
607 Ok(escape_string_json(&str_))
608}
609
610#[jrsonnet_macros::builtin]
611fn builtin_manifest_json_ex(
612 s: State,
613 value: Any,
614 indent: IStr,
615 newline: Option<IStr>,
616 key_val_sep: Option<IStr>,
617 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
618) -> Result<String> {
619 let newline = newline.as_deref().unwrap_or("\n");
620 let key_val_sep = key_val_sep.as_deref().unwrap_or(": ");
621 manifest_json_ex(
622 s,
623 &value.0,
624 &ManifestJsonOptions {
625 padding: &indent,
626 mtype: ManifestType::Std,
627 newline,
628 key_val_sep,
629 #[cfg(feature = "exp-preserve-order")]
630 preserve_order: preserve_order.unwrap_or(false),
631 },
632 )
633}
634
635#[jrsonnet_macros::builtin]
636fn builtin_manifest_yaml_doc(
637 s: State,
638 value: Any,
639 indent_array_in_object: Option<bool>,
640 quote_keys: Option<bool>,
641 #[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
642) -> Result<String> {
643 manifest_yaml_ex(
644 s,
645 &value.0,
646 &ManifestYamlOptions {
647 padding: " ",
648 arr_element_padding: if indent_array_in_object.unwrap_or(false) {
649 " "
650 } else {
651 ""
652 },
653 quote_keys: quote_keys.unwrap_or(true),
654 #[cfg(feature = "exp-preserve-order")]
655 preserve_order: preserve_order.unwrap_or(false),
656 },
657 )
658}
659
660#[jrsonnet_macros::builtin]
661fn builtin_reverse(value: ArrValue) -> Result<ArrValue> {
662 Ok(value.reversed())
663}
664
665#[jrsonnet_macros::builtin]
666fn builtin_str_replace(str: String, from: IStr, to: IStr) -> Result<String> {
667 Ok(str.replace(&from as &str, &to as &str))
668}
669
670#[jrsonnet_macros::builtin]
671fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> Result<VecVal> {
672 use Either2::*;
673 Ok(VecVal(Cc::new(match maxsplits {
674 A(n) => str
675 .splitn(n + 1, &c as &str)
676 .map(|s| Val::Str(s.into()))
677 .collect(),
678 B(_) => str.split(&c as &str).map(|s| Val::Str(s.into())).collect(),
679 })))
680}
681
682#[jrsonnet_macros::builtin]
683fn builtin_ascii_upper(str: IStr) -> Result<String> {
684 Ok(str.to_ascii_uppercase())
685}
686
687#[jrsonnet_macros::builtin]
688fn builtin_ascii_lower(str: IStr) -> Result<String> {
689 Ok(str.to_ascii_lowercase())
690}
691
692#[jrsonnet_macros::builtin]
693fn builtin_member(s: State, arr: IndexableVal, x: Any) -> Result<bool> {
694 match arr {
695 IndexableVal::Str(str) => {
696 let x: IStr = IStr::from_untyped(x.0, s)?;
697 Ok(!x.is_empty() && str.contains(&*x))
698 }
699 IndexableVal::Arr(a) => {
700 for item in a.iter(s.clone()) {
701 let item = item?;
702 if equals(s.clone(), &item, &x.0)? {
703 return Ok(true);
704 }
705 }
706 Ok(false)
707 }
708 }
709}
710
711#[jrsonnet_macros::builtin]
712fn builtin_count(s: State, arr: Vec<Any>, v: Any) -> Result<usize> {
713 let mut count = 0;
714 for item in &arr {
715 if equals(s.clone(), &item.0, &v.0)? {
716 count += 1;
717 }
718 }
719 Ok(count)
720}
721
722#[jrsonnet_macros::builtin]
723fn builtin_any(s: State, arr: ArrValue) -> Result<bool> {
724 for v in arr.iter(s.clone()) {
725 let v = bool::from_untyped(v?, s.clone())?;
726 if v {
727 return Ok(true);
728 }
729 }
730 Ok(false)
731}
732
733#[jrsonnet_macros::builtin]
734fn builtin_all(s: State, arr: ArrValue) -> Result<bool> {
735 for v in arr.iter(s.clone()) {
736 let v = bool::from_untyped(v?, s.clone())?;
737 if !v {
738 return Ok(false);
739 }
740 }
741 Ok(true)
742}
74325
deletedcrates/jrsonnet-evaluator/src/stdlib/sort.rsdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/src/trace/location.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
1mod location;
2
3use std::path::{Path, PathBuf};1use std::path::{Path, PathBuf};
42
5use jrsonnet_parser::Source;3use jrsonnet_parser::{CodeLocation, Source};
6pub use location::*;
74
8use crate::{error::Error, LocError, State};5use crate::{error::Error, LocError, State};
96
10/// The way paths should be displayed7/// The way paths should be displayed
8#[derive(Clone)]
11pub enum PathResolver {9pub enum PathResolver {
12 /// Only filename10 /// Only filename
13 FileName,11 FileName,
18}16}
1917
20impl PathResolver {18impl PathResolver {
19 /// Will return `Self::Relative(cwd)`, or `Self::Absolute` on cwd failure
20 pub fn new_cwd_fallback() -> Self {
21 std::env::current_dir().map_or(Self::Absolute, Self::Relative)
22 }
21 pub fn resolve(&self, from: &Path) -> String {23 pub fn resolve(&self, from: &Path) -> String {
22 match self {24 match self {
23 Self::FileName => from25 Self::FileName => from
84 fn write_trace(86 fn write_trace(
85 &self,87 &self,
86 out: &mut dyn std::fmt::Write,88 out: &mut dyn std::fmt::Write,
87 s: &State,89 _s: &State,
88 error: &LocError,90 error: &LocError,
89 ) -> Result<(), std::fmt::Error> {91 ) -> Result<(), std::fmt::Error> {
90 write!(out, "{}", error.error())?;92 write!(out, "{}", error.error())?;
91 if let Error::ImportSyntaxError {93 if let Error::ImportSyntaxError { path, error } = error.error() {
92 path,
93 source_code,
94 error,
95 } = error.error()
96 {
97 use std::fmt::Write;94 use std::fmt::Write;
9895
99 writeln!(out)?;96 writeln!(out)?;
100 let mut n = match path.repr() {97 let mut n = path.source_path().path().map_or_else(
98 || path.source_path().to_string(),
101 Ok(r) => self.resolver.resolve(r),99 |r| self.resolver.resolve(r),
102 Err(v) => v.to_string(),
103 };100 );
104 let mut offset = error.location.offset;101 let mut offset = error.location.offset;
105 let is_eof = if offset >= source_code.len() {102 let is_eof = if offset >= path.code().len() {
106 offset = source_code.len().saturating_sub(1);103 offset = path.code().len().saturating_sub(1);
107 true104 true
108 } else {105 } else {
109 false106 false
110 };107 };
111 let mut location = offset_to_location(source_code, &[offset as u32])108 let mut location = path
109 .map_source_locations(&[offset as u32])
112 .into_iter()110 .into_iter()
113 .next()111 .next()
114 .unwrap();112 .unwrap();
118116
119 write!(n, ":").unwrap();117 write!(n, ":").unwrap();
120 print_code_location(&mut n, &location, &location).unwrap();118 print_code_location(&mut n, &location, &location).unwrap();
121 write!(out, "{:<p$}{}", "", n, p = self.padding,)?;119 write!(out, "{:<p$}{n}", "", p = self.padding)?;
122 }120 }
123 let file_names = error121 let file_names = error
124 .trace()122 .trace()
129 use std::fmt::Write;127 use std::fmt::Write;
130 #[allow(clippy::option_if_let_else)]128 #[allow(clippy::option_if_let_else)]
131 if let Some(location) = location {129 if let Some(location) = location {
132 let mut resolved_path = match location.0.repr() {130 let mut resolved_path = match location.0.source_path().path() {
133 Ok(r) => self.resolver.resolve(r),131 Some(r) => self.resolver.resolve(r),
134 Err(v) => v.to_string(),132 None => location.0.source_path().to_string(),
135 };133 };
136 // TODO: Process all trace elements first134 // TODO: Process all trace elements first
137 let location =135 let location = location.0.map_source_locations(&[location.1, location.2]);
138 s.map_source_locations(location.0.clone(), &[location.1, location.2]);
139 write!(resolved_path, ":").unwrap();136 write!(resolved_path, ":").unwrap();
140 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();137 print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();
141 write!(resolved_path, ":").unwrap();138 write!(resolved_path, ":").unwrap();
176 fn write_trace(173 fn write_trace(
177 &self,174 &self,
178 out: &mut dyn std::fmt::Write,175 out: &mut dyn std::fmt::Write,
179 s: &State,176 _s: &State,
180 error: &LocError,177 error: &LocError,
181 ) -> Result<(), std::fmt::Error> {178 ) -> Result<(), std::fmt::Error> {
182 write!(out, "{}", error.error())?;179 write!(out, "{}", error.error())?;
183 for item in &error.trace().0 {180 for item in &error.trace().0 {
184 writeln!(out)?;181 writeln!(out)?;
185 let desc = &item.desc;182 let desc = &item.desc;
186 if let Some(source) = &item.location {183 if let Some(source) = &item.location {
187 let start_end = s.map_source_locations(source.0.clone(), &[source.1, source.2]);184 let start_end = source.0.map_source_locations(&[source.1, source.2]);
188 let resolved_path = match source.0.repr() {185 let resolved_path = source.0.source_path().path().map_or_else(
189 Ok(r) => r.display().to_string(),186 || source.0.source_path().to_string(),
190 Err(v) => v.to_string(),187 |r| r.display().to_string(),
191 };188 );
192189
193 write!(190 write!(
194 out,191 out,
195 " at {} ({}:{}:{})",192 " at {} ({}:{}:{})",
196 desc, resolved_path, start_end[0].line, start_end[0].column,193 desc, resolved_path, start_end[0].line, start_end[0].column,
197 )?;194 )?;
198 } else {195 } else {
199 write!(out, " during {}", desc)?;196 write!(out, " during {desc}")?;
200 }197 }
201 }198 }
202 Ok(())199 Ok(())
213 fn write_trace(210 fn write_trace(
214 &self,211 &self,
215 out: &mut dyn std::fmt::Write,212 out: &mut dyn std::fmt::Write,
216 s: &State,213 _s: &State,
217 error: &LocError,214 error: &LocError,
218 ) -> Result<(), std::fmt::Error> {215 ) -> Result<(), std::fmt::Error> {
219 write!(out, "{}", error.error())?;216 write!(out, "{}", error.error())?;
220 if let Error::ImportSyntaxError {217 if let Error::ImportSyntaxError { path, error } = error.error() {
221 path,
222 source_code,
223 error,
224 } = error.error()
225 {
226 writeln!(out)?;218 writeln!(out)?;
227 let offset = error.location.offset;219 let offset = error.location.offset;
228 let location = offset_to_location(source_code, &[offset as u32])220 let location = path
221 .map_source_locations(&[offset as u32])
229 .into_iter()222 .into_iter()
230 .next()223 .next()
231 .unwrap();224 .unwrap();
234227
235 self.print_snippet(228 self.print_snippet(
236 out,229 out,
237 source_code,230 path.code(),
238 path,231 path,
239 &location,232 &location,
240 &end_location,233 &end_location,
246 writeln!(out)?;239 writeln!(out)?;
247 let desc = &item.desc;240 let desc = &item.desc;
248 if let Some(source) = &item.location {241 if let Some(source) = &item.location {
249 let start_end = s.map_source_locations(source.0.clone(), &[source.1, source.2]);242 let start_end = source.0.map_source_locations(&[source.1, source.2]);
250 self.print_snippet(243 self.print_snippet(
251 out,244 out,
252 &s.get_source(source.0.clone()).unwrap(),245 source.0.code(),
253 &source.0,246 &source.0,
254 &start_end[0],247 &start_end[0],
255 &start_end[1],248 &start_end[1],
256 desc,249 desc,
257 )?;250 )?;
258 } else {251 } else {
259 write!(out, "{}", desc)?;252 write!(out, "{desc}")?;
260 }253 }
261 }254 }
262 Ok(())255 Ok(())
284 .take(end.line_end_offset - end.line_start_offset)277 .take(end.line_end_offset - end.line_start_offset)
285 .collect();278 .collect();
286279
287 let origin = match origin.repr() {280 let origin = origin.source_path().path().map_or_else(
281 || origin.source_path().to_string(),
288 Ok(r) => self.resolver.resolve(r),282 |r| self.resolver.resolve(r),
289 Err(v) => v.to_string(),
290 };283 );
291 let snippet = Snippet {284 let snippet = Snippet {
292 opt: FormatOptions {285 opt: FormatOptions {
293 color: true,286 color: true,
312 };305 };
313306
314 let dl = DisplayList::from(snippet);307 let dl = DisplayList::from(snippet);
315 write!(out, "{}", dl)?;308 write!(out, "{dl}")?;
316309
317 Ok(())310 Ok(())
318 }311 }
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
393 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};393 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};
394 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};394 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};
395}395}
396pub use Either;
396397
397pub type MyType = Either![u32, f64, String];398pub type MyType = Either![u32, f64, String];
398399
modifiedcrates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth
21 UnionFailed(ComplexValType, TypeLocErrorList),21 UnionFailed(ComplexValType, TypeLocErrorList),
22 #[error(22 #[error(
23 "number out of bounds: {0} not in {}..{}",23 "number out of bounds: {0} not in {}..{}",
24 .1.map(|v|v.to_string()).unwrap_or_else(|| "".to_owned()),24 .1.map(|v|v.to_string()).unwrap_or_default(),
25 .2.map(|v|v.to_string()).unwrap_or_else(|| "".to_owned()),25 .2.map(|v|v.to_string()).unwrap_or_default(),
26 )]26 )]
27 BoundsFailed(f64, Option<f64>, Option<f64>),27 BoundsFailed(f64, Option<f64>, Option<f64>),
28}28}
65 writeln!(f)?;65 writeln!(f)?;
66 }66 }
67 out.clear();67 out.clear();
68 write!(out, "{}", err)?;68 write!(out, "{err}")?;
6969
70 for (i, line) in out.lines().enumerate() {70 for (i, line) in out.lines().enumerate() {
71 if line.trim().is_empty() {71 if line.trim().is_empty() {
77 writeln!(f)?;77 writeln!(f)?;
78 write!(f, " ")?;78 write!(f, " ")?;
79 }79 }
80 write!(f, "{}", line)?;80 write!(f, "{line}")?;
81 }81 }
82 }82 }
83 Ok(())83 Ok(())
125impl Display for ValuePathItem {125impl Display for ValuePathItem {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 match self {127 match self {
128 Self::Field(name) => write!(f, ".{:?}", name)?,128 Self::Field(name) => write!(f, ".{name:?}")?,
129 Self::Index(idx) => write!(f, "[{}]", idx)?,129 Self::Index(idx) => write!(f, "[{idx}]")?,
130 }130 }
131 Ok(())131 Ok(())
132 }132 }
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 write!(f, "self")?;139 write!(f, "self")?;
140 for elem in self.0.iter().rev() {140 for elem in self.0.iter().rev() {
141 write!(f, "{}", elem)?;141 write!(f, "{elem}")?;
142 }142 }
143 Ok(())143 Ok(())
144 }144 }
171 for (i, item) in a.iter(s.clone()).enumerate() {171 for (i, item) in a.iter(s.clone()).enumerate() {
172 push_type_description(172 push_type_description(
173 s.clone(),173 s.clone(),
174 || format!("array index {}", i),174 || format!("array index {i}"),
175 || ValuePathItem::Index(i as u64),175 || ValuePathItem::Index(i as u64),
176 || elem_type.check(s.clone(), &item.clone()?),176 || elem_type.check(s.clone(), &item.clone()?),
177 )?;177 )?;
185 for (i, item) in a.iter(s.clone()).enumerate() {185 for (i, item) in a.iter(s.clone()).enumerate() {
186 push_type_description(186 push_type_description(
187 s.clone(),187 s.clone(),
188 || format!("array index {}", i),188 || format!("array index {i}"),
189 || ValuePathItem::Index(i as u64),189 || ValuePathItem::Index(i as u64),
190 || elem_type.check(s.clone(), &item.clone()?),190 || elem_type.check(s.clone(), &item.clone()?),
191 )?;191 )?;
200 if let Some(got_v) = obj.get(s.clone(), (*k).into())? {200 if let Some(got_v) = obj.get(s.clone(), (*k).into())? {
201 push_type_description(201 push_type_description(
202 s.clone(),202 s.clone(),
203 || format!("property {}", k),203 || format!("property {k}"),
204 || ValuePathItem::Field((*k).into()),204 || ValuePathItem::Field((*k).into()),
205 || v.check(s.clone(), &got_v),205 || v.check(s.clone(), &got_v),
206 )?;206 )?;
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
12 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,12 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,
13 },13 },
14 throw, ObjValue, Result, State, Unbound, WeakObjValue,14 throw,
15 typed::BoundedUsize,
16 ObjValue, Result, State, Unbound, WeakObjValue,
15};17};
1618
184 }186 }
185}187}
186188
189/// Represents a Jsonnet array value.
187#[derive(Debug, Clone, Trace)]190#[derive(Debug, Clone, Trace)]
188// may contrain other ArrValue191// may contrain other ArrValue
189#[trace(tracking(force))]192#[trace(tracking(force))]
190pub enum ArrValue {193pub enum ArrValue {
194 /// Layout optimized byte array.
191 Bytes(#[trace(skip)] IBytes),195 Bytes(#[trace(skip)] IBytes),
196 /// Every element is lazy evaluated.
192 Lazy(Cc<Vec<Thunk<Val>>>),197 Lazy(Cc<Vec<Thunk<Val>>>),
198 /// Every field is already evaluated.
193 Eager(Cc<Vec<Val>>),199 Eager(Cc<Vec<Val>>),
200 /// Concatenation of two arrays of any kind.
194 Extended(Box<(Self, Self)>),201 Extended(Box<(Self, Self)>),
202 /// Represents a integer array in form `[start, start + 1, ... end - 1, end]`.
203 /// This kind of arrays is generated by `std.range(start, end)` call, and used for loops.
195 Range(i32, i32),204 Range(i32, i32),
205 /// Sliced array view.
196 Slice(Box<Slice>),206 Slice(Box<Slice>),
207 /// Reversed array view.
208 /// Returned by `std.reverse(other)` call
197 Reversed(Box<Self>),209 Reversed(Box<Self>),
198}210}
199211
204 pub fn new_eager() -> Self {216 pub fn new_eager() -> Self {
205 Self::Eager(Cc::new(Vec::new()))217 Self::Eager(Cc::new(Vec::new()))
206 }218 }
219 pub fn empty() -> Self {
220 Self::new_range(0, 0)
221 }
207222
208 /// # Panics223 /// # Panics
209 /// If a > b224 /// If a > b
225 #[inline]
210 pub fn new_range(a: i32, b: i32) -> Self {226 pub fn new_range(a: i32, b: i32) -> Self {
211 assert!(a <= b);227 assert!(a <= b);
212 Self::Range(a, b)228 Self::Range(a, b)
231 }))247 }))
232 }248 }
233249
250 /// Array length.
234 pub fn len(&self) -> usize {251 pub fn len(&self) -> usize {
235 match self {252 match self {
236 Self::Bytes(i) => i.len(),253 Self::Bytes(i) => i.len(),
243 }260 }
244 }261 }
245262
263 /// Is array contains no elements?
246 pub fn is_empty(&self) -> bool {264 pub fn is_empty(&self) -> bool {
247 self.len() == 0265 self.len() == 0
248 }266 }
249267
268 /// Get array element by index, evaluating it, if it is lazy.
269 ///
270 /// Returns `None` on out-of-bounds condition.
250 pub fn get(&self, s: State, index: usize) -> Result<Option<Val>> {271 pub fn get(&self, s: State, index: usize) -> Result<Option<Val>> {
251 match self {272 match self {
252 Self::Bytes(i) => i273 Self::Bytes(i) => i
286 if index >= v.to() {307 if index >= v.to() {
287 return Ok(None);308 return Ok(None);
288 }309 }
289 v.inner.get(s, index as usize)310 v.inner.get(s, index)
290 }311 }
291 }312 }
292 }313 }
293314
315 /// Get array element by index, without evaluation.
316 ///
317 /// Returns `None` on out-of-bounds condition.
294 pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {318 pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
295 match self {319 match self {
296 Self::Bytes(i) => i320 Self::Bytes(i) => i
326 if index >= s.to() {350 if index >= s.to() {
327 return None;351 return None;
328 }352 }
329 s.inner.get_lazy(index as usize)353 s.inner.get_lazy(index)
330 }354 }
331 }355 }
332 }356 }
333357
358 /// Evaluate all array elements, returning new array.
334 pub fn evaluated(&self, s: State) -> Result<Cc<Vec<Val>>> {359 pub fn evaluated(&self, s: State) -> Result<Cc<Vec<Val>>> {
335 Ok(match self {360 Ok(match self {
336 Self::Bytes(i) => {361 Self::Bytes(i) => {
383 })408 })
384 }409 }
385410
411 /// Iterate over elements, evaluating them.
386 pub fn iter(&self, s: State) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {412 pub fn iter(&self, s: State) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {
387 (0..self.len()).map(move |idx| match self {413 (0..self.len()).map(move |idx| match self {
388 Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))),414 Self::Bytes(b) => Ok(Val::Num(f64::from(b[idx]))),
394 })420 })
395 }421 }
396422
423 /// Iterate over elements, returning lazy values.
397 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = Thunk<Val>> + '_ {424 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = Thunk<Val>> + '_ {
398 (0..self.len()).map(move |idx| match self {425 (0..self.len()).map(move |idx| match self {
399 Self::Bytes(b) => Thunk::evaluated(Val::Num(f64::from(b[idx]))),426 Self::Bytes(b) => Thunk::evaluated(Val::Num(f64::from(b[idx]))),
405 })432 })
406 }433 }
407434
435 /// Return a reversed view on current array.
408 #[must_use]436 #[must_use]
409 pub fn reversed(self) -> Self {437 pub fn reversed(self) -> Self {
410 Self::Reversed(Box::new(self))438 Self::Reversed(Box::new(self))
411 }439 }
412440
441 /// Return a new array, produced by passing every element of current array to specified callback function.
413 pub fn map(self, s: State, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {442 pub fn map(self, s: State, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {
414 let mut out = Vec::with_capacity(self.len());443 let mut out = Vec::with_capacity(self.len());
415444
420 Ok(Self::Eager(Cc::new(out)))449 Ok(Self::Eager(Cc::new(out)))
421 }450 }
422451
452 /// Return a new array, produced from current array by removing every value, for which specified callback function returns false.
423 pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {453 pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {
424 let mut out = Vec::with_capacity(self.len());454 let mut out = Vec::with_capacity(self.len());
425455
454 }484 }
455}485}
456486
487/// Represents a Jsonnet value, which can be spliced or indexed (string or array).
457#[allow(clippy::module_name_repetitions)]488#[allow(clippy::module_name_repetitions)]
458pub enum IndexableVal {489pub enum IndexableVal {
490 /// String.
459 Str(IStr),491 Str(IStr),
492 /// Array.
460 Arr(ArrValue),493 Arr(ArrValue),
461}494}
462495impl IndexableVal {
496 /// Slice the value.
497 ///
498 /// # Implementation
499 ///
500 /// For strings, will create a copy of specified interval.
501 ///
502 /// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.
503 pub fn slice(
504 self,
505 index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
506 end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
507 step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
508 ) -> Result<Self> {
509 match &self {
510 IndexableVal::Str(s) => {
511 let index = index.as_deref().copied().unwrap_or(0);
512 let end = end.as_deref().copied().unwrap_or(usize::MAX);
513 let step = step.as_deref().copied().unwrap_or(1);
514
515 if index >= end {
516 return Ok(Self::Str("".into()));
517 }
518
519 Ok(Self::Str(
520 (s.chars()
521 .skip(index)
522 .take(end - index)
523 .step_by(step)
524 .collect::<String>())
525 .into(),
526 ))
527 }
528 IndexableVal::Arr(arr) => {
529 let index = index.as_deref().copied().unwrap_or(0);
530 let end = end.as_deref().copied().unwrap_or(usize::MAX).min(arr.len());
531 let step = step.as_deref().copied().unwrap_or(1);
532
533 if index >= end {
534 return Ok(Self::Arr(ArrValue::new_eager()));
535 }
536
537 Ok(Self::Arr(ArrValue::Slice(Box::new(Slice {
538 inner: arr.clone(),
539 from: index as u32,
540 to: end as u32,
541 step: step as u32,
542 }))))
543 }
544 }
545 }
546}
547
548/// Represents any valid Jsonnet value.
463#[derive(Debug, Clone, Trace)]549#[derive(Debug, Clone, Trace)]
464pub enum Val {550pub enum Val {
551 /// Represents a Jsonnet boolean.
465 Bool(bool),552 Bool(bool),
553 /// Represents a Jsonnet null value.
466 Null,554 Null,
555 /// Represents a Jsonnet string.
467 Str(IStr),556 Str(IStr),
557 /// Represents a Jsonnet number.
558 /// Should be finite, and not NaN
559 /// This restriction isn't enforced by enum, as enum field can't be marked as private
468 Num(f64),560 Num(f64),
561 /// Represents a Jsonnet array.
469 Arr(ArrValue),562 Arr(ArrValue),
563 /// Represents a Jsonnet object.
470 Obj(ObjValue),564 Obj(ObjValue),
565 /// Represents a Jsonnet function.
471 Func(FuncVal),566 Func(FuncVal),
472}567}
473568
474#[cfg(target_pointer_width = "64")]569impl From<IndexableVal> for Val {
570 fn from(v: IndexableVal) -> Self {
571 match v {
572 IndexableVal::Str(s) => Self::Str(s),
475static_assertions::assert_eq_size!(Val, [u8; 32]);573 IndexableVal::Arr(a) => Self::Arr(a),
574 }
575 }
576}
577
578// Broken between stable and nightly, as there is new layout size optimization
579// #[cfg(target_pointer_width = "64")]
580// static_assertions::assert_eq_size!(Val, [u8; 24]);
476581
477impl Val {582impl Val {
478 pub const fn as_bool(&self) -> Option<bool> {583 pub const fn as_bool(&self) -> Option<bool> {
deletedcrates/jrsonnet-evaluator/tests/as_native.rsdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/builtin.rsdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/common.rsdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden.rsdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/array_comp.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/array_comp.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/builtin_json.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/builtin_json.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/builtin_json_minified.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/builtin_json_minified.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/builtin_parseJson.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/builtin_parseJson.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/issue23.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/issue23.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/issue40.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/issue40.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/missing_binding.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/missing_binding.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/object_comp.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/object_comp.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/test_assertThrow.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/golden/test_assertThrow.jsonnet.goldendiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/sanity.rsdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite.rsdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/builtin_ascii.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/builtin_base64.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/builtin_chars.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/builtin_constant.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/builtin_count.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/builtin_join.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/builtin_member.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/function_args.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/function_context.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/function_lazy_args.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/local.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/math.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/object_assertion.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/object_comp_self.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/object_context.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/object_fields.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/object_inheritance.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/object_locals.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/object_super_standalone.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/sjsonnet_issue_127.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/suite/string_concat.jsonnetdiffbeforeafterboth

no changes

deletedcrates/jrsonnet-evaluator/tests/typed_obj.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-interner/Cargo.tomldiffbeforeafterboth
7edition = "2021"7edition = "2021"
88
9[features]9[features]
10default = ["serde"]10default = []
11# Implement value serialization using structdump
12structdump = ["dep:structdump"]
13# Implement value serialization using serde
14#
15# Warning: serialized values won't be deduplicated
11serde = ["dep:serde"]16serde = ["dep:serde"]
1217
13[dependencies]18[dependencies]
14jrsonnet-gcmodule = { version = "0.3.4" }19jrsonnet-gcmodule = { version = "0.3.4" }
1520
16serde = { version = "1.0", optional = true }21serde = { version = "1.0", optional = true }
22structdump = { version = "0.2.0", optional = true }
23
17rustc-hash = "1.1"24rustc-hash = "1.1"
18hashbrown = { version = "0.12.1", features = ["inline-more"] }25hashbrown = { version = "0.12.1", features = ["inline-more"] }
modifiedcrates/jrsonnet-interner/src/inner.rsdiffbeforeafterboth
84 unsafe { Self::new_raw(str.as_bytes(), true) }84 unsafe { Self::new_raw(str.as_bytes(), true) }
85 }85 }
8686
87 // `slice::from_raw_parts` is not yet stabilized
88 #[allow(clippy::missing_const_for_fn)]
87 pub fn as_slice(&self) -> &[u8] {89 pub fn as_slice(&self) -> &[u8] {
88 let header = Self::header(self);90 let header = Self::header(self);
89 // SAFETY: data is not null, and it is correctly initialized91 // SAFETY: data is not null, and it is correctly initialized
modifiedcrates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth
32}32}
3333
34impl IStr {34impl IStr {
35 #[must_use]
36 pub fn empty() -> Self {
37 "".into()
38 }
35 #[must_use]39 #[must_use]
36 pub fn as_str(&self) -> &str {40 pub fn as_str(&self) -> &str {
37 self as &str41 self as &str
201 }205 }
202}206}
203207
208#[cfg(feature = "serde")]
204impl serde::Serialize for IStr {209impl serde::Serialize for IStr {
205 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>210 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
206 where211 where
210 }215 }
211}216}
212217
218#[cfg(feature = "serde")]
213impl<'de> serde::Deserialize<'de> for IStr {219impl<'de> serde::Deserialize<'de> for IStr {
214 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>220 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
215 where221 where
220 }226 }
221}227}
228
229#[cfg(feature = "structdump")]
230impl structdump::Codegen for IStr {
231 fn gen_code(
232 &self,
233 res: &mut structdump::CodegenResult,
234 _unique: bool,
235 ) -> structdump::TokenStream {
236 let s: &str = self;
237 res.add_code(
238 structdump::quote! {
239 structdump_import::IStr::from(#s)
240 },
241 Some(structdump::quote![structdump_import::IStr]),
242 false,
243 )
244 }
245}
222246
223thread_local! {247thread_local! {
224 static POOL: RefCell<HashMap<Inner, (), BuildHasherDefault<FxHasher>>> = RefCell::new(HashMap::with_capacity_and_hasher(200, BuildHasherDefault::default()));248 static POOL: RefCell<HashMap<Inner, (), BuildHasherDefault<FxHasher>>> = RefCell::new(HashMap::with_capacity_and_hasher(200, BuildHasherDefault::default()));
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
122 Normal {122 Normal {
123 ty: Box<Type>,123 ty: Box<Type>,
124 is_option: bool,124 is_option: bool,
125 name: String,125 name: Option<String>,
126 cfg_attrs: Vec<Attribute>,126 cfg_attrs: Vec<Attribute>,
127 // ident: Ident,127 // ident: Ident,
128 },128 },
129 Lazy {129 Lazy {
130 is_option: bool,130 is_option: bool,
131 name: String,131 name: Option<String>,
132 },132 },
133 State,133 State,
134 Location,134 Location,
142 FnArg::Typed(a) => a,142 FnArg::Typed(a) => a,
143 };143 };
144 let ident = match &arg.pat as &Pat {144 let ident = match &arg.pat as &Pat {
145 Pat::Ident(i) => i.ident.clone(),145 Pat::Ident(i) => Some(i.ident.clone()),
146 _ => return Err(Error::new(arg.pat.span(), "arg should be plain identifier")),146 _ => None,
147 };147 };
148 let ty = &arg.ty;148 let ty = &arg.ty;
149 if type_is_path(ty, "State").is_some() {149 if type_is_path(ty, "State").is_some() {
153 } else if type_is_path(ty, "Thunk").is_some() {153 } else if type_is_path(ty, "Thunk").is_some() {
154 return Ok(Self::Lazy {154 return Ok(Self::Lazy {
155 is_option: false,155 is_option: false,
156 name: ident.to_string(),156 name: ident.map(|v| v.to_string()),
157 });157 });
158 }158 }
159159
166 if type_is_path(ty, "Thunk").is_some() {166 if type_is_path(ty, "Thunk").is_some() {
167 return Ok(Self::Lazy {167 return Ok(Self::Lazy {
168 is_option: true,168 is_option: true,
169 name: ident.to_string(),169 name: ident.map(|v| v.to_string()),
170 });170 });
171 }171 }
172172
185 Ok(Self::Normal {185 Ok(Self::Normal {
186 ty,186 ty,
187 is_option,187 is_option,
188 name: ident.to_string(),188 name: ident.map(|v| v.to_string()),
189 cfg_attrs,189 cfg_attrs,
190 })190 })
191 }191 }
248 name,248 name,
249 cfg_attrs,249 cfg_attrs,
250 ..250 ..
251 } => Some(quote! {251 } => {
252 let name = name
253 .as_ref()
254 .map(|n| quote! {Some(std::borrow::Cow::Borrowed(#n))})
255 .unwrap_or_else(|| quote! {None});
256 Some(quote! {
252 #(#cfg_attrs)*257 #(#cfg_attrs)*
253 BuiltinParam {258 BuiltinParam {
254 name: std::borrow::Cow::Borrowed(#name),259 name: #name,
255 has_default: #is_option,260 has_default: #is_option,
256 },261 },
257 }),262 })
263 }
258 ArgInfo::Lazy { is_option, name } => Some(quote! {264 ArgInfo::Lazy { is_option, name } => {
265 let name = name
266 .as_ref()
267 .map(|n| quote! {Some(std::borrow::Cow::Borrowed(#n))})
268 .unwrap_or_else(|| quote! {None});
269 Some(quote! {
259 BuiltinParam {270 BuiltinParam {
260 name: std::borrow::Cow::Borrowed(#name),271 name: #name,
261 has_default: #is_option,272 has_default: #is_option,
262 },273 },
263 }),274 })
275 }
264 ArgInfo::State => None,276 ArgInfo::State => None,
265 ArgInfo::Location => None,277 ArgInfo::Location => None,
266 ArgInfo::This => None,278 ArgInfo::This => None,
267 });279 });
268280
281 let mut id = 0usize;
269 let pass = args.iter().map(|a| match a {282 let pass = args
283 .iter()
284 .map(|a| match a {
285 ArgInfo::Normal { .. } | ArgInfo::Lazy { .. } => {
286 let cid = id;
287 id += 1;
288 (quote! {#cid}, a)
289 }
290 ArgInfo::State | ArgInfo::Location | ArgInfo::This => {
291 (quote! {compile_error!("should not use id")}, a)
292 }
293 })
294 .map(|(id, a)| match a {
270 ArgInfo::Normal {295 ArgInfo::Normal {
271 ty,296 ty,
272 is_option,297 is_option,
273 name,298 name,
274 cfg_attrs,299 cfg_attrs,
275 } => {300 } => {
301 let name = name.as_ref().map(|v| v.as_str()).unwrap_or("<unnamed>");
276 let eval = quote! {s.push_description(302 let eval = quote! {s.push_description(
277 || format!("argument <{}> evaluation", #name),303 || format!("argument <{}> evaluation", #name),
278 || <#ty>::from_untyped(value.evaluate(s.clone())?, s.clone()),304 || <#ty>::from_untyped(value.evaluate(s.clone())?, s.clone()),
279 )?};305 )?};
280 let value = if *is_option {306 let value = if *is_option {
281 quote! {if let Some(value) = parsed.get(#name) {307 quote! {if let Some(value) = &parsed[#id] {
282 Some(#eval)308 Some(#eval)
283 } else {309 } else {
284 None310 None
285 },}311 },}
286 } else {312 } else {
287 quote! {{313 quote! {{
288 let value = parsed.get(#name).expect("args shape is checked");314 let value = parsed[#id].as_ref().expect("args shape is checked");
289 #eval315 #eval
290 },}316 },}
291 };317 };
294 #value320 #value
295 }321 }
296 }322 }
297 ArgInfo::Lazy { is_option, name } => {323 ArgInfo::Lazy { is_option, .. } => {
298 if *is_option {324 if *is_option {
299 quote! {if let Some(value) = parsed.get(#name) {325 quote! {if let Some(value) = &parsed[#id] {
300 Some(value.clone())326 Some(value.clone())
301 } else {327 } else {
302 None328 None
303 }}329 }}
304 } else {330 } else {
305 quote! {331 quote! {
306 parsed.get(#name).expect("args shape is correct").clone(),332 parsed[#id].as_ref().expect("args shape is correct").clone(),
307 }333 }
308 }334 }
309 }335 }
modifiedcrates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth
7edition = "2021"7edition = "2021"
88
9[features]9[features]
10default = []
10exp-destruct = []11exp-destruct = []
12# Implement serialization of AST using structdump
13#
14# Structdump generates code, which exactly replicated passed AST
15# Contrary to serde, has no code bloat problem, and is recommended
16#
17# The only limitation is serialized form is only useable if built from build script
18structdump = ["dep:structdump", "jrsonnet-interner/structdump"]
19# Implement serialization of AST using serde
20#
21# Warning: as serde doesn't deduplicate strings, `Source` struct will bloat
22# output binary with repeating source code. To resolve this issue, you should either
23# override serialization of this struct using custom `Serializer`/`Deserializer`,
24# not rely on Source, and fill its `source_code` with empty value, or use `structdump`
25# instead
26serde = ["dep:serde"]
1127
12[dependencies]28[dependencies]
13jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }29jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
1935
20serde = { version = "1.0", features = ["derive", "rc"], optional = true }36serde = { version = "1.0", features = ["derive", "rc"], optional = true }
21
22[dev-dependencies]
23jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.4.2" }37structdump = { version = "0.2.0", features = ["derive"], optional = true }
2438
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
8use jrsonnet_interner::IStr;8use jrsonnet_interner::IStr;
9#[cfg(feature = "serde")]9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};10use serde::{Deserialize, Serialize};
11#[cfg(feature = "structdump")]
12use structdump::Codegen;
1113
12use crate::source::Source;14use crate::source::Source;
1315
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17#[cfg_attr(feature = "structdump", derive(Codegen))]
15#[derive(Debug, PartialEq, Trace)]18#[derive(Debug, PartialEq, Trace)]
16pub enum FieldName {19pub enum FieldName {
17 /// {fixed: 2}20 /// {fixed: 2}
20 Dyn(LocExpr),23 Dyn(LocExpr),
21}24}
2225
26#[cfg_attr(feature = "structdump", derive(Codegen))]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]28#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
25pub enum Visibility {29pub enum Visibility {
37 }41 }
38}42}
3943
44#[cfg_attr(feature = "structdump", derive(Codegen))]
40#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41#[derive(Clone, Debug, PartialEq, Trace)]46#[derive(Clone, Debug, PartialEq, Trace)]
42pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);47pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
4348
49#[cfg_attr(feature = "structdump", derive(Codegen))]
44#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45#[derive(Debug, PartialEq, Trace)]51#[derive(Debug, PartialEq, Trace)]
46pub struct FieldMember {52pub struct FieldMember {
51 pub value: LocExpr,57 pub value: LocExpr,
52}58}
5359
60#[cfg_attr(feature = "structdump", derive(Codegen))]
54#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]61#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55#[derive(Debug, PartialEq, Trace)]62#[derive(Debug, PartialEq, Trace)]
56pub enum Member {63pub enum Member {
59 AssertStmt(AssertStmt),66 AssertStmt(AssertStmt),
60}67}
6168
69#[cfg_attr(feature = "structdump", derive(Codegen))]
62#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]71#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
64pub enum UnaryOpType {72pub enum UnaryOpType {
84 }92 }
85}93}
8694
95#[cfg_attr(feature = "structdump", derive(Codegen))]
87#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]97#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
89pub enum BinaryOpType {98pub enum BinaryOpType {
150}159}
151160
152/// name, default value161/// name, default value
162#[cfg_attr(feature = "structdump", derive(Codegen))]
153#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]163#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
154#[derive(Debug, PartialEq, Trace)]164#[derive(Debug, PartialEq, Trace)]
155pub struct Param(pub Destruct, pub Option<LocExpr>);165pub struct Param(pub Destruct, pub Option<LocExpr>);
156166
157/// Defined function parameters167/// Defined function parameters
168#[cfg_attr(feature = "structdump", derive(Codegen))]
158#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
159#[derive(Debug, Clone, PartialEq, Trace)]170#[derive(Debug, Clone, PartialEq, Trace)]
160pub struct ParamsDesc(pub Rc<Vec<Param>>);171pub struct ParamsDesc(pub Rc<Vec<Param>>);
166 }177 }
167}178}
168179
180#[cfg_attr(feature = "structdump", derive(Codegen))]
169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]181#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
170#[derive(Debug, PartialEq, Trace)]182#[derive(Debug, PartialEq, Trace)]
171pub struct ArgsDesc {183pub struct ArgsDesc {
187 Drop,199 Drop,
188}200}
189201
202#[cfg_attr(feature = "structdump", derive(Codegen))]
190#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]203#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
191#[derive(Debug, Clone, PartialEq, Trace)]204#[derive(Debug, Clone, PartialEq, Trace)]
192pub enum Destruct {205pub enum Destruct {
216 }229 }
217}230}
218231
232#[cfg_attr(feature = "structdump", derive(Codegen))]
219#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
220#[derive(Debug, Clone, PartialEq, Trace)]234#[derive(Debug, Clone, PartialEq, Trace)]
221pub enum BindSpec {235pub enum BindSpec {
230 },244 },
231}245}
232246
247#[cfg_attr(feature = "structdump", derive(Codegen))]
233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]248#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
234#[derive(Debug, PartialEq, Trace)]249#[derive(Debug, PartialEq, Trace)]
235pub struct IfSpecData(pub LocExpr);250pub struct IfSpecData(pub LocExpr);
236251
252#[cfg_attr(feature = "structdump", derive(Codegen))]
237#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]253#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
238#[derive(Debug, PartialEq, Trace)]254#[derive(Debug, PartialEq, Trace)]
239pub struct ForSpecData(pub IStr, pub LocExpr);255pub struct ForSpecData(pub IStr, pub LocExpr);
240256
257#[cfg_attr(feature = "structdump", derive(Codegen))]
241#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]258#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
242#[derive(Debug, PartialEq, Trace)]259#[derive(Debug, PartialEq, Trace)]
243pub enum CompSpec {260pub enum CompSpec {
244 IfSpec(IfSpecData),261 IfSpec(IfSpecData),
245 ForSpec(ForSpecData),262 ForSpec(ForSpecData),
246}263}
247264
265#[cfg_attr(feature = "structdump", derive(Codegen))]
248#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]266#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
249#[derive(Debug, PartialEq, Trace)]267#[derive(Debug, PartialEq, Trace)]
250pub struct ObjComp {268pub struct ObjComp {
256 pub compspecs: Vec<CompSpec>,274 pub compspecs: Vec<CompSpec>,
257}275}
258276
277#[cfg_attr(feature = "structdump", derive(Codegen))]
259#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]278#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
260#[derive(Debug, PartialEq, Trace)]279#[derive(Debug, PartialEq, Trace)]
261pub enum ObjBody {280pub enum ObjBody {
262 MemberList(Vec<Member>),281 MemberList(Vec<Member>),
263 ObjComp(ObjComp),282 ObjComp(ObjComp),
264}283}
265284
285#[cfg_attr(feature = "structdump", derive(Codegen))]
266#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]286#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
267#[derive(Debug, PartialEq, Eq, Clone, Copy, Trace)]287#[derive(Debug, PartialEq, Eq, Clone, Copy, Trace)]
268pub enum LiteralType {288pub enum LiteralType {
274 False,294 False,
275}295}
276296
297#[cfg_attr(feature = "structdump", derive(Codegen))]
277#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]298#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
278#[derive(Debug, PartialEq, Trace)]299#[derive(Debug, PartialEq, Trace)]
279pub struct SliceDesc {300pub struct SliceDesc {
283}304}
284305
285/// Syntax base306/// Syntax base
307#[cfg_attr(feature = "structdump", derive(Codegen))]
286#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]308#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
287#[derive(Debug, PartialEq, Trace)]309#[derive(Debug, PartialEq, Trace)]
288pub enum Expr {310pub enum Expr {
341 Index(LocExpr, LocExpr),363 Index(LocExpr, LocExpr),
342 /// function(x) x364 /// function(x) x
343 Function(ParamsDesc, LocExpr),365 Function(ParamsDesc, LocExpr),
344 /// std.thisFile
345 IntrinsicThisFile,
346 /// std.id,
347 IntrinsicId,
348 /// std.primitiveEquals
349 Intrinsic(IStr),
350 /// if true == false then 1 else 2366 /// if true == false then 1 else 2
351 IfElse {367 IfElse {
352 cond: IfSpecData,368 cond: IfSpecData,
357}373}
358374
359/// file, begin offset, end offset375/// file, begin offset, end offset
376#[cfg_attr(feature = "structdump", derive(Codegen))]
360#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]377#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
361#[derive(Clone, PartialEq, Eq, Trace)]378#[derive(Clone, PartialEq, Eq, Trace)]
362#[trace(skip)]379#[trace(skip)]
379396
380/// Holds AST expression and its location in source file397/// Holds AST expression and its location in source file
381#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]398#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
399#[cfg_attr(feature = "structdump", derive(Codegen))]
382#[derive(Clone, PartialEq, Trace)]400#[derive(Clone, PartialEq, Trace)]
383pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);401pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);
384402
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
7pub use expr::*;7pub use expr::*;
8pub use jrsonnet_interner::IStr;8pub use jrsonnet_interner::IStr;
9pub use peg;9pub use peg;
10mod location;
10mod source;11mod source;
11mod unescape;12mod unescape;
13pub use location::CodeLocation;
12pub use source::Source;14pub use source::{Source, SourceDirectory, SourceFile, SourcePath, SourcePathT, SourceVirtual};
1315
14pub struct ParserSettings {16pub struct ParserSettings {
15 pub file_name: Source,17 pub file_name: Source,
193 / assertion:assertion(s) {expr::Member::AssertStmt(assertion)}195 / assertion:assertion(s) {expr::Member::AssertStmt(assertion)}
194 / field:field(s) {expr::Member::Field(field)}196 / field:field(s) {expr::Member::Field(field)}
195 pub rule objinside(s: &ParserSettings) -> expr::ObjBody197 pub rule objinside(s: &ParserSettings) -> expr::ObjBody
196 = pre_locals:(b: obj_local(s) comma() {b})* "[" _ key:expr(s) _ "]" _ plus:"+"? _ ":" _ value:expr(s) post_locals:(comma() b:obj_local(s) {b})* _ forspec:forspec(s) others:(_ rest:compspec(s) {rest})? {198 = pre_locals:(b: obj_local(s) comma() {b})* "[" _ key:expr(s) _ "]" _ plus:"+"? _ ":" _ value:expr(s) post_locals:(comma() b:obj_local(s) {b})* _ ("," _)? forspec:forspec(s) others:(_ rest:compspec(s) {rest})? {
197 let mut compspecs = vec![CompSpec::ForSpec(forspec)];199 let mut compspecs = vec![CompSpec::ForSpec(forspec)];
198 compspecs.extend(others.unwrap_or_default());200 compspecs.extend(others.unwrap_or_default());
199 expr::ObjBody::ObjComp(expr::ObjComp{201 expr::ObjBody::ObjComp(expr::ObjComp{
252 pub rule expr_basic(s: &ParserSettings) -> Expr254 pub rule expr_basic(s: &ParserSettings) -> Expr
253 = literal(s)255 = literal(s)
254
255 / quiet!{"$intrinsicThisFile" {Expr::IntrinsicThisFile}}
256 / quiet!{"$intrinsicId" {Expr::IntrinsicId}}
257 / quiet!{"$intrinsic(" name:id() ")" {Expr::Intrinsic(name)}}
258256
259 / string_expr(s) / number_expr(s)257 / string_expr(s) / number_expr(s)
260 / array_expr(s)258 / array_expr(s)
362360
363#[cfg(test)]361#[cfg(test)]
364pub mod tests {362pub mod tests {
365 use std::borrow::Cow;363 use jrsonnet_interner::IStr;
366
367 use BinaryOpType::*;364 use BinaryOpType::*;
368365
374 parse(371 parse(
375 $s,372 $s,
376 &ParserSettings {373 &ParserSettings {
377 file_name: Source::new_virtual(Cow::Borrowed("<test>")),374 file_name: Source::new_virtual("<test>".into(), IStr::empty()),
378 },375 },
379 )376 )
380 .unwrap()377 .unwrap()
385 ($expr:expr, $from:expr, $to:expr$(,)?) => {382 ($expr:expr, $from:expr, $to:expr$(,)?) => {
386 LocExpr(383 LocExpr(
387 std::rc::Rc::new($expr),384 std::rc::Rc::new($expr),
388 ExprLocation(Source::new_virtual(Cow::Borrowed("<test>")), $from, $to),385 ExprLocation(
386 Source::new_virtual("<test>".into(), IStr::empty()),
387 $from,
388 $to,
389 ),
714 parse!("local x(foo = 'foo', bar) = null; null");715 parse!("local x(foo = 'foo', bar) = null; null");
715 }716 }
716
717 #[test]
718 fn can_parse_stdlib() {
719 parse!(jrsonnet_stdlib::STDLIB_STR);
720 }
721717
722 #[test]718 #[test]
723 fn add_location_info_to_all_sub_expressions() {719 fn add_location_info_to_all_sub_expressions() {
724 use Expr::*;720 use Expr::*;
725721
726 let file_name = Source::new_virtual(Cow::Borrowed("<test>"));722 let file_name = Source::new_virtual("<test>".into(), IStr::empty());
727 let expr = parse(723 let expr = parse(
728 "{} { local x = 1, x: x } + {}",724 "{} { local x = 1, x: x } + {}",
729 &ParserSettings { file_name },725 &ParserSettings { file_name },
addedcrates/jrsonnet-parser/src/location.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-parser/src/source.rsdiffbeforeafterboth
1use std::{1use std::{
2 borrow::Cow,2 any::Any,
3 fmt,3 fmt::{self, Debug, Display},
4 hash::{Hash, Hasher},
4 path::{Component, Path, PathBuf},5 path::{Path, PathBuf},
5 rc::Rc,6 rc::Rc,
6};7};
78
8use jrsonnet_gcmodule::{Trace, Tracer};9use jrsonnet_gcmodule::{Trace, Tracer};
10use jrsonnet_interner::IStr;
9#[cfg(feature = "serde")]11#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};12use serde::{Deserialize, Serialize};
1113#[cfg(feature = "structdump")]
14use structdump::Codegen;
15
16use crate::location::{location_to_offset, offset_to_location, CodeLocation};
17
18macro_rules! any_ext_methods {
19 ($T:ident) => {
20 fn as_any(&self) -> &dyn Any;
21 fn dyn_hash(&self, hasher: &mut dyn Hasher);
22 fn dyn_eq(&self, other: &dyn $T) -> bool;
23 fn dyn_debug(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
24 };
25}
26macro_rules! any_ext_impl {
27 ($T:ident) => {
28 fn as_any(&self) -> &dyn Any {
29 self
30 }
31 fn dyn_hash(&self, mut hasher: &mut dyn Hasher) {
32 self.hash(&mut hasher)
33 }
34 fn dyn_eq(&self, other: &dyn $T) -> bool {
35 let other = if let Some(v) = other.as_any().downcast_ref::<Self>() {
36 v
37 } else {
38 return false;
39 };
40 let this = <Self as $T>::as_any(self)
41 .downcast_ref::<Self>()
42 .expect("restricted by impl");
43 this == other
44 }
45 fn dyn_debug(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 <Self as std::fmt::Debug>::fmt(self, fmt)
47 }
48 };
49}
50macro_rules! any_ext {
51 ($T:ident) => {
52 impl Hash for dyn $T {
53 fn hash<H: Hasher>(&self, state: &mut H) {
54 self.dyn_hash(state)
55 }
56 }
57 impl PartialEq for dyn $T {
58 fn eq(&self, other: &Self) -> bool {
59 self.dyn_eq(other)
60 }
61 }
62 impl Eq for dyn $T {}
63 };
64}
65pub trait SourcePathT: Trace + Debug + Display {
66 /// This method should be checked by resolver before panicking with bad SourcePath input
67 /// if `true` - then resolver may threat this path as default, and default is usally a CWD
68 fn is_default(&self) -> bool;
69 fn path(&self) -> Option<&Path>;
70 any_ext_methods!(SourcePathT);
71}
72any_ext!(SourcePathT);
73
74/// Represents location of a file
75///
76/// Standard CLI only operates using
77/// - [`SourceFile`] - for any file
78/// - [`SourceDirectory`] - for resolution from CWD
79/// - [`SourceVirtual`] - for stdlib/ext-str
80///
81/// From all of those, only [`SourceVirtual`] may be constructed manually, any other path kind should be only obtained
82/// from assigned `ImportResolver`
83/// However, you should always check `is_default` method return, as it will return true for any paths, where default
84/// search location is applicable
85///
86/// Resolver may also return custom implementations of this trait, for example it may return http url in case of remotely loaded files
87#[derive(Eq, Debug, Clone)]
88pub struct SourcePath(Rc<dyn SourcePathT>);
89impl SourcePath {
90 pub fn new(inner: impl SourcePathT) -> Self {
91 Self(Rc::new(inner))
92 }
93 pub fn downcast_ref<T: SourcePathT>(&self) -> Option<&T> {
94 self.0.as_any().downcast_ref()
95 }
96 pub fn is_default(&self) -> bool {
97 self.0.is_default()
98 }
99 pub fn path(&self) -> Option<&Path> {
100 self.0.path()
101 }
102}
103impl Hash for SourcePath {
104 fn hash<H: Hasher>(&self, state: &mut H) {
105 self.0.hash(state);
106 }
107}
108impl PartialEq for SourcePath {
109 #[allow(clippy::op_ref)]
110 fn eq(&self, other: &Self) -> bool {
111 &*self.0 == &*other.0
112 }
113}
114impl Trace for SourcePath {
115 fn trace(&self, tracer: &mut Tracer) {
116 (*self.0).trace(tracer)
117 }
118
119 fn is_type_tracked() -> bool
120 where
121 Self: Sized,
122 {
123 true
124 }
125}
126impl Display for SourcePath {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "{}", self.0)
129 }
130}
131impl Default for SourcePath {
132 fn default() -> Self {
133 Self(Rc::new(SourceDefault))
134 }
135}
136
137#[cfg(feature = "structdump")]
138impl Codegen for SourcePath {
139 fn gen_code(
140 &self,
141 res: &mut structdump::CodegenResult,
142 unique: bool,
143 ) -> structdump::TokenStream {
144 let source_virtual = self
145 .0
146 .as_any()
147 .downcast_ref::<SourceVirtual>()
148 .expect("can only codegen for virtual source paths!")
149 .0
150 .clone();
151 let val = res.add_value(source_virtual, false);
152 res.add_code(
153 structdump::quote! {
154 structdump_import::SourcePath::new(structdump_import::SourceVirtual(#val))
155 },
156 Some(structdump::quote!(SourcePath)),
157 unique,
158 )
159 }
160}
161
162#[derive(Trace, Hash, PartialEq, Eq, Debug)]
163struct SourceDefault;
164impl Display for SourceDefault {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(f, "<default>")
167 }
168}
169impl SourcePathT for SourceDefault {
170 fn is_default(&self) -> bool {
171 true
172 }
173 fn path(&self) -> Option<&Path> {
174 None
175 }
176 any_ext_impl!(SourcePathT);
177}
178
179/// Represents path to the file on the disk
180/// Directories shouldn't be put here, as resolution for files differs from resolution for directories:
181///
182/// When `file` is being resolved from `SourceFile(a/b/c)`, it should be resolved to `SourceFile(a/b/file)`,
183/// however if it is being resolved from `SourceDirectory(a/b/c)`, then it should be resolved to `SourceDirectory(a/b/c/file)`
184#[derive(Trace, Hash, PartialEq, Eq, Debug)]
185pub struct SourceFile(PathBuf);
186impl SourceFile {
187 pub fn new(path: PathBuf) -> Self {
188 Self(path)
189 }
190 pub fn path(&self) -> &Path {
191 &self.0
192 }
193}
194impl Display for SourceFile {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 write!(f, "{}", self.0.display())
197 }
198}
199impl SourcePathT for SourceFile {
200 fn is_default(&self) -> bool {
201 false
202 }
203 fn path(&self) -> Option<&Path> {
204 Some(&self.0)
205 }
206 any_ext_impl!(SourcePathT);
207}
208
209/// Represents path to the directory on the disk
210///
211/// See also [`SourceFile`]
212#[derive(Trace, Hash, PartialEq, Eq, Debug)]
213pub struct SourceDirectory(PathBuf);
214impl SourceDirectory {
215 pub fn new(path: PathBuf) -> Self {
216 Self(path)
217 }
218 pub fn path(&self) -> &Path {
219 &self.0
220 }
221}
222impl Display for SourceDirectory {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 write!(f, "{}", self.0.display())
225 }
226}
227impl SourcePathT for SourceDirectory {
228 fn is_default(&self) -> bool {
229 false
230 }
231 fn path(&self) -> Option<&Path> {
232 Some(&self.0)
233 }
234 any_ext_impl!(SourcePathT);
235}
236
237/// Represents virtual file, whose are located in memory, and shouldn't be cached
238///
239/// It is used for --ext-code=.../--tla-code=.../standard library source code by default,
240/// and user can construct arbitrary values by hand, without asking import resolver
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]241#[cfg_attr(feature = "structdump", derive(Codegen))]
13#[derive(PartialEq, Eq, Debug, Hash)]242#[derive(Trace, Hash, PartialEq, Eq, Debug, Clone)]
14enum Inner {
15 Real(PathBuf),243pub struct SourceVirtual(pub IStr);
16 Virtual(Cow<'static, str>),244impl Display for SourceVirtual {
17}245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 write!(f, "{}", self.0)
247 }
248}
249impl SourcePathT for SourceVirtual {
250 fn is_default(&self) -> bool {
251 true
252 }
253 fn path(&self) -> Option<&Path> {
254 None
255 }
256 any_ext_impl!(SourcePathT);
257}
18258
19/// Either real file, or virtual259/// Either real file, or virtual
20/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut260/// Hash of FileName always have same value as raw Path, to make it possible to use with raw_entry_mut
261#[cfg_attr(feature = "structdump", derive(Codegen))]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]262#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22#[derive(Clone, PartialEq, Eq, Debug)]263#[derive(Clone, PartialEq, Eq, Debug)]
23pub struct Source(Rc<Inner>);264pub struct Source(pub Rc<(SourcePath, IStr)>);
24static_assertions::assert_eq_size!(Source, *const ());265static_assertions::assert_eq_size!(Source, *const ());
25266
26impl Trace for Source {267impl Trace for Source {
32}273}
33274
34impl Source {275impl Source {
35 /// Fails when path contains inner /../ or /./ references, or not absolute
36 pub fn new(path: PathBuf) -> Option<Self> {276 pub fn new(path: SourcePath, code: IStr) -> Self {
37 if !path.is_absolute()
38 || path
39 .components()
40 .any(|c| matches!(c, Component::CurDir | Component::ParentDir))
41 {
42 return None;
43 }
44 Some(Self(Rc::new(Inner::Real(path))))277 Self(Rc::new((path, code)))
45 }278 }
46279
47 pub fn new_virtual(n: Cow<'static, str>) -> Self {280 pub fn new_virtual(name: IStr, code: IStr) -> Self {
48 Self(Rc::new(Inner::Virtual(n)))281 Self::new(SourcePath::new(SourceVirtual(name)), code)
49 }282 }
50283
51 pub fn short_display(&self) -> ShortDisplay {284 pub fn code(&self) -> &str {
52 ShortDisplay(self.clone())
53 }
54 pub fn full_path(&self) -> String {
55 match self.inner() {285 &self.0 .1
56 Inner::Real(r) => r.display().to_string(),
57 Inner::Virtual(v) => v.to_string(),
58 }
59 }286 }
60287
61 /// Returns None if file is virtual
62 pub fn path(&self) -> Option<&Path> {288 pub fn source_path(&self) -> &SourcePath {
63 match self.inner() {289 &self.0 .0
64 Inner::Real(r) => Some(r),
65 Inner::Virtual(_) => None,
66 }
67 }290 }
291
68 pub fn repr(&self) -> Result<&Path, &str> {292 pub fn map_source_locations(&self, locs: &[u32]) -> Vec<CodeLocation> {
69 match self.inner() {293 offset_to_location(&self.0 .1, locs)
70 Inner::Real(r) => Ok(r),
71 Inner::Virtual(v) => Err(v.as_ref()),
72 }
73 }294 }
74
75 fn inner(&self) -> &Inner {295 pub fn map_from_source_location(&self, line: usize, column: usize) -> Option<usize> {
76 &self.0 as &Inner296 location_to_offset(&self.0 .1, line, column)
77 }297 }
78}298}
79pub struct ShortDisplay(Source);
80impl fmt::Display for ShortDisplay {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match &self.0 .0 as &Inner {
83 Inner::Real(r) => {
84 write!(
85 f,
86 "{}",
87 r.file_name().expect("path is valid").to_string_lossy()
88 )
89 }
90 Inner::Virtual(n) => write!(f, "{}", n),
91 }
92 }
93}
94299
modifiedcrates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth
7edition = "2021"7edition = "2021"
88
9[features]9[features]
10default = ["codegenerated-stdlib"]
11# Speed-up initialization by generating code for parsed stdlib, instead
12# of invoking parser for it
13codegenerated-stdlib = ["jrsonnet-parser/structdump"]
14# Enables legacy `std.thisFile` support, at the cost of worse caching
15legacy-this-file = []
16# Add order preservation flag to some functions
17exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]
18# Preserve order for files parsed via `std.parseJson`
19# Shame it isn't possible to enable per parse call, instead of globally
20exp-serde-preserve-order = [
21 "serde_json/preserve_order",
22 "jrsonnet-evaluator/exp-serde-preserve-order",
23]
1024
11[dependencies]25[dependencies]
26jrsonnet-evaluator = { path = "../jrsonnet-evaluator", features = [
27 # std.parseJson parses file via serde, then converts Value to evaluator Val
28 "serde_json",
29], version = "0.4.2" }
30jrsonnet-macros = { path = "../jrsonnet-macros", version = "0.4.2" }
31jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
32jrsonnet-gcmodule = "0.3.4"
33
34# Used for stdlib AST serialization
35bincode = { version = "1.3", optional = true }
36# Used both for stdlib AST serialization and std.parseJson/std.parseYaml
37serde = "1.0"
38
39# std.md5
40md5 = "0.7.0"
41# std.base64
42base64 = "0.13.0"
43# std.parseJson
44serde_json = "1.0"
45# std.parseYaml, custom library fork is used for C++/golang compatibility
46serde_yaml_with_quirks = "0.8.24"
47
48[build-dependencies]
49jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
50structdump = { version = "0.2.0", features = ["derive"] }
1251
deletedcrates/jrsonnet-stdlib/README.mddiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/build.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/encoding.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/expr.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/hash.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
1use std::{
2 cell::{Ref, RefCell, RefMut},
3 collections::HashMap,
4 rc::Rc,
5};
6
7use jrsonnet_evaluator::{
8 error::{Error::*, Result},
9 function::{builtin::Builtin, ArgLike, CallLocation, FuncVal, TlaArg},
10 gc::{GcHashMap, TraceBox},
11 tb, throw_runtime,
12 trace::PathResolver,
13 typed::{Any, Either, Either2, Either4, VecVal, M1},
14 val::{equals, ArrValue},
15 Context, ContextBuilder, IStr, ObjValue, ObjValueBuilder, State, Thunk, Val,
16};
17use jrsonnet_gcmodule::Cc;
18use jrsonnet_macros::builtin;
19use jrsonnet_parser::Source;
20
21mod expr;
22mod types;
23pub use types::*;
24mod arrays;
25pub use arrays::*;
26mod math;
27pub use math::*;
28mod operator;
29pub use operator::*;
30mod sort;
31pub use sort::*;
32mod hash;
33pub use hash::*;
34mod encoding;
35pub use encoding::*;
36mod objects;
37pub use objects::*;
38mod manifest;
39pub use manifest::*;
40mod parse;
41pub use parse::*;
42
43pub fn stdlib_uncached(s: State, settings: Rc<RefCell<Settings>>) -> ObjValue {
44 let mut builder = ObjValueBuilder::new();
45
46 let expr = expr::stdlib_expr();
47 let eval = jrsonnet_evaluator::evaluate(s.clone(), Context::default(), &expr)
48 .expect("stdlib.jsonnet should have no errors")
49 .as_obj()
50 .expect("stdlib.jsonnet should evaluate to object");
51
52 builder.with_super(eval);
53
54 for (name, builtin) in [
55 ("length", builtin_length::INST),
56 // Types
57 ("type", builtin_type::INST),
58 ("isString", builtin_is_string::INST),
59 ("isNumber", builtin_is_number::INST),
60 ("isBoolean", builtin_is_boolean::INST),
61 ("isObject", builtin_is_object::INST),
62 ("isArray", builtin_is_array::INST),
63 ("isFunction", builtin_is_function::INST),
64 // Arrays
65 ("makeArray", builtin_make_array::INST),
66 ("slice", builtin_slice::INST),
67 ("map", builtin_map::INST),
68 ("flatMap", builtin_flatmap::INST),
69 ("filter", builtin_filter::INST),
70 ("foldl", builtin_foldl::INST),
71 ("foldr", builtin_foldr::INST),
72 ("range", builtin_range::INST),
73 ("join", builtin_join::INST),
74 ("reverse", builtin_reverse::INST),
75 ("any", builtin_any::INST),
76 ("all", builtin_all::INST),
77 ("member", builtin_member::INST),
78 ("count", builtin_count::INST),
79 // Math
80 ("modulo", builtin_modulo::INST),
81 ("floor", builtin_floor::INST),
82 ("ceil", builtin_ceil::INST),
83 ("log", builtin_log::INST),
84 ("pow", builtin_pow::INST),
85 ("sqrt", builtin_sqrt::INST),
86 ("sin", builtin_sin::INST),
87 ("cos", builtin_cos::INST),
88 ("tan", builtin_tan::INST),
89 ("asin", builtin_asin::INST),
90 ("acos", builtin_acos::INST),
91 ("atan", builtin_atan::INST),
92 ("exp", builtin_exp::INST),
93 ("mantissa", builtin_mantissa::INST),
94 ("exponent", builtin_exponent::INST),
95 // Operator
96 ("mod", builtin_mod::INST),
97 ("primitiveEquals", builtin_primitive_equals::INST),
98 ("equals", builtin_equals::INST),
99 ("format", builtin_format::INST),
100 // Sort
101 ("sort", builtin_sort::INST),
102 // Hash
103 ("md5", builtin_md5::INST),
104 // Encoding
105 ("encodeUTF8", builtin_encode_utf8::INST),
106 ("decodeUTF8", builtin_decode_utf8::INST),
107 ("base64", builtin_base64::INST),
108 ("base64Decode", builtin_base64_decode::INST),
109 ("base64DecodeBytes", builtin_base64_decode_bytes::INST),
110 // Objects
111 ("objectFieldsEx", builtin_object_fields_ex::INST),
112 ("objectHasEx", builtin_object_has_ex::INST),
113 // Manifest
114 ("escapeStringJson", builtin_escape_string_json::INST),
115 ("manifestJsonEx", builtin_manifest_json_ex::INST),
116 ("manifestYamlDoc", builtin_manifest_yaml_doc::INST),
117 // Parsing
118 ("parseJson", builtin_parse_json::INST),
119 ("parseYaml", builtin_parse_yaml::INST),
120 // Misc
121 ("codepoint", builtin_codepoint::INST),
122 ("substr", builtin_substr::INST),
123 ("char", builtin_char::INST),
124 ("strReplace", builtin_str_replace::INST),
125 ("splitLimit", builtin_splitlimit::INST),
126 ("asciiUpper", builtin_ascii_upper::INST),
127 ("asciiLower", builtin_ascii_lower::INST),
128 ("findSubstr", builtin_find_substr::INST),
129 ("startsWith", builtin_starts_with::INST),
130 ("endsWith", builtin_ends_with::INST),
131 ]
132 .iter()
133 .cloned()
134 {
135 builder
136 .member(name.into())
137 .hide()
138 .value(s.clone(), Val::Func(FuncVal::StaticBuiltin(builtin)))
139 .expect("no conflict");
140 }
141
142 builder
143 .member("extVar".into())
144 .hide()
145 .value(
146 s.clone(),
147 Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_ext_var {
148 settings: settings.clone()
149 })))),
150 )
151 .expect("no conflict");
152 builder
153 .member("native".into())
154 .hide()
155 .value(
156 s.clone(),
157 Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_native {
158 settings: settings.clone()
159 })))),
160 )
161 .expect("no conflict");
162 builder
163 .member("trace".into())
164 .hide()
165 .value(
166 s.clone(),
167 Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_trace { settings })))),
168 )
169 .expect("no conflict");
170
171 builder
172 .member("id".into())
173 .hide()
174 .value(s, Val::Func(FuncVal::Id))
175 .expect("no conflict");
176
177 builder.build()
178}
179
180pub trait TracePrinter {
181 fn print_trace(&self, s: State, loc: CallLocation, value: IStr);
182}
183
184pub struct StdTracePrinter {
185 resolver: PathResolver,
186}
187impl StdTracePrinter {
188 pub fn new(resolver: PathResolver) -> Self {
189 Self { resolver }
190 }
191}
192impl TracePrinter for StdTracePrinter {
193 fn print_trace(&self, _s: State, loc: CallLocation, value: IStr) {
194 eprint!("TRACE:");
195 if let Some(loc) = loc.0 {
196 let locs = loc.0.map_source_locations(&[loc.1]);
197 eprint!(
198 " {}:{}",
199 match loc.0.source_path().path() {
200 Some(p) => self.resolver.resolve(p),
201 None => loc.0.source_path().to_string(),
202 },
203 locs[0].line
204 );
205 }
206 eprintln!(" {}", value);
207 }
208}
209
210pub struct Settings {
211 /// Used for `std.extVar`
212 pub ext_vars: HashMap<IStr, TlaArg>,
213 /// Used for `std.native`
214 pub ext_natives: HashMap<IStr, Cc<TraceBox<dyn Builtin>>>,
215 /// Helper to add globals without implementing custom ContextInitializer
216 pub globals: GcHashMap<IStr, Thunk<Val>>,
217 /// Used for `std.trace`
218 pub trace_printer: Box<dyn TracePrinter>,
219 /// Used for `std.thisFile`
220 pub path_resolver: PathResolver,
221}
222
1pub const STDLIB_STR: &str = include_str!("./std.jsonnet");223pub fn extvar_source(name: &str, code: impl Into<IStr>) -> Source {
224 let source_name = format!("<extvar:{}>", name);
225 Source::new_virtual(source_name.into(), code.into())
226}
227
228pub struct ContextInitializer {
229 // When we don't need to support legacy-this-file, we can reuse same context for all files
230 #[cfg(not(feature = "legacy-this-file"))]
231 context: Context,
232 // Otherwise, we can only keep first stdlib layer, and then stack thisFile on top of it
233 #[cfg(feature = "legacy-this-file")]
234 stdlib_obj: ObjValue,
235 settings: Rc<RefCell<Settings>>,
236}
237impl ContextInitializer {
238 pub fn new(s: State, resolver: PathResolver) -> Self {
239 let settings = Settings {
240 ext_vars: Default::default(),
241 ext_natives: Default::default(),
242 globals: Default::default(),
243 trace_printer: Box::new(StdTracePrinter::new(resolver.clone())),
244 path_resolver: resolver,
245 };
246 let settings = Rc::new(RefCell::new(settings));
247 Self {
248 #[cfg(not(feature = "legacy-this-file"))]
249 context: {
250 let mut context = ContextBuilder::with_capacity(1);
251 context.bind(
252 "std".into(),
253 Thunk::evaluated(Val::Obj(stdlib_uncached(s, settings.clone()))),
254 );
255 context.build()
256 },
257 #[cfg(feature = "legacy-this-file")]
258 stdlib_obj: stdlib_uncached(s, settings.clone()),
259 settings,
260 }
261 }
262 pub fn settings(&self) -> Ref<Settings> {
263 self.settings.borrow()
264 }
265 pub fn settings_mut(&self) -> RefMut<Settings> {
266 self.settings.borrow_mut()
267 }
268 pub fn add_ext_var(&self, name: IStr, value: Val) {
269 self.settings_mut()
270 .ext_vars
271 .insert(name, TlaArg::Val(value));
272 }
273 pub fn add_ext_str(&self, name: IStr, value: IStr) {
274 self.settings_mut()
275 .ext_vars
276 .insert(name, TlaArg::String(value));
277 }
278 pub fn add_ext_code(&self, name: &str, code: impl Into<IStr>) -> Result<()> {
279 let code = code.into();
280 let source = extvar_source(name, code.clone());
281 let parsed = jrsonnet_parser::parse(
282 &code,
283 &jrsonnet_parser::ParserSettings {
284 file_name: source.clone(),
285 },
286 )
287 .map_err(|e| ImportSyntaxError {
288 path: source,
289 error: Box::new(e),
290 })?;
291 // self.data_mut().volatile_files.insert(source_name, code);
292 self.settings_mut()
293 .ext_vars
294 .insert(name.into(), TlaArg::Code(parsed));
295 Ok(())
296 }
297 pub fn add_native(&self, name: IStr, cb: Cc<TraceBox<dyn Builtin>>) {
298 self.settings_mut().ext_natives.insert(name, cb);
299 }
300}
301impl jrsonnet_evaluator::ContextInitializer for ContextInitializer {
302 #[cfg(not(feature = "legacy-this-file"))]
303 fn initialize(&self, _s: State, _source: Source) -> jrsonnet_evaluator::Context {
304 let out = self.context.clone();
305 let globals = &self.settings().globals;
306 if globals.is_empty() {
307 return out;
308 }
309
310 let mut out = ContextBuilder::extend(out);
311 for (k, v) in globals.iter() {
312 out.bind(k.clone(), v.clone());
313 }
314 out.build()
315 }
316 #[cfg(feature = "legacy-this-file")]
317 fn initialize(&self, s: State, source: Source) -> jrsonnet_evaluator::Context {
318 let mut builder = ObjValueBuilder::new();
319 builder.with_super(self.stdlib_obj.clone());
320 builder
321 .member("thisFile".into())
322 .hide()
323 .value(
324 s,
325 Val::Str(match source.source_path().path() {
326 Some(p) => self.settings().path_resolver.resolve(p).into(),
327 None => source.source_path().to_string().into(),
328 }),
329 )
330 .expect("this object builder is empty");
331 let stdlib_with_this_file = builder.build();
332
333 let mut context = ContextBuilder::with_capacity(1);
334 context.bind(
335 "std".into(),
336 Thunk::evaluated(Val::Obj(stdlib_with_this_file)),
337 );
338 for (k, v) in self.settings().globals.iter() {
339 context.bind(k.clone(), v.clone());
340 }
341 context.build()
342 }
343 fn as_any(&self) -> &dyn std::any::Any {
344 self
345 }
346}
347
348#[builtin]
349fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> Result<usize> {
350 use Either4::*;
351 Ok(match x {
352 A(x) => x.chars().count(),
353 B(x) => x.len(),
354 C(x) => x.len(),
355 D(f) => f.params_len(),
356 })
357}
358
359#[builtin]
360const fn builtin_codepoint(str: char) -> Result<u32> {
361 Ok(str as u32)
362}
363
364#[builtin]
365fn builtin_substr(str: IStr, from: usize, len: usize) -> Result<String> {
366 Ok(str.chars().skip(from).take(len).collect())
367}
368
369#[builtin(fields(
370 settings: Rc<RefCell<Settings>>,
371))]
372fn builtin_ext_var(this: &builtin_ext_var, s: State, x: IStr) -> Result<Any> {
373 let ctx = s.create_default_context(extvar_source(&x, ""));
374 Ok(Any(this
375 .settings
376 .borrow()
377 .ext_vars
378 .get(&x)
379 .cloned()
380 .ok_or_else(|| UndefinedExternalVariable(x))?
381 .evaluate_arg(s.clone(), ctx, true)?
382 .evaluate(s)?))
383}
384
385#[builtin(fields(
386 settings: Rc<RefCell<Settings>>,
387))]
388fn builtin_native(this: &builtin_native, name: IStr) -> Result<Any> {
389 Ok(Any(this
390 .settings
391 .borrow()
392 .ext_natives
393 .get(&name)
394 .cloned()
395 .map_or(Val::Null, |v| {
396 Val::Func(FuncVal::Builtin(v.clone()))
397 })))
398}
399
400#[builtin]
401fn builtin_char(n: u32) -> Result<char> {
402 Ok(std::char::from_u32(n).ok_or_else(|| InvalidUnicodeCodepointGot(n))?)
403}
404
405#[builtin(fields(
406 settings: Rc<RefCell<Settings>>,
407))]
408fn builtin_trace(
409 this: &builtin_trace,
410 s: State,
411 loc: CallLocation,
412 str: IStr,
413 rest: Thunk<Val>,
414) -> Result<Any> {
415 this.settings
416 .borrow()
417 .trace_printer
418 .print_trace(s.clone(), loc, str);
419 Ok(Any(rest.evaluate(s)?))
420}
421
422#[builtin]
423fn builtin_str_replace(str: String, from: IStr, to: IStr) -> Result<String> {
424 Ok(str.replace(&from as &str, &to as &str))
425}
426
427#[builtin]
428fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> Result<VecVal> {
429 use Either2::*;
430 Ok(VecVal(Cc::new(match maxsplits {
431 A(n) => str
432 .splitn(n + 1, &c as &str)
433 .map(|s| Val::Str(s.into()))
434 .collect(),
435 B(_) => str.split(&c as &str).map(|s| Val::Str(s.into())).collect(),
436 })))
437}
438
439#[builtin]
440fn builtin_ascii_upper(str: IStr) -> Result<String> {
441 Ok(str.to_ascii_uppercase())
442}
443
444#[builtin]
445fn builtin_ascii_lower(str: IStr) -> Result<String> {
446 Ok(str.to_ascii_lowercase())
447}
448
449#[builtin]
450fn builtin_find_substr(pat: IStr, str: IStr) -> Result<ArrValue> {
451 if pat.is_empty() || str.is_empty() || pat.len() > str.len() {
452 return Ok(ArrValue::empty());
453 }
454
455 let str = str.as_str();
456 let pat = pat.as_bytes();
457 let strb = str.as_bytes();
458
459 let max_pos = str.len() - pat.len();
460
461 let mut out: Vec<Val> = Vec::new();
462 for (ch_idx, (i, _)) in str
463 .char_indices()
464 .take_while(|(i, _)| i <= &max_pos)
465 .enumerate()
466 {
467 if &strb[i..i + pat.len()] == pat {
468 out.push(Val::Num(ch_idx as f64))
469 }
470 }
471 Ok(out.into())
472}
473
474#[allow(clippy::comparison_chain)]
475#[builtin]
476fn builtin_starts_with(
477 s: State,
478 a: Either![IStr, ArrValue],
479 b: Either![IStr, ArrValue],
480) -> Result<bool> {
481 Ok(match (a, b) {
482 (Either2::A(a), Either2::A(b)) => a.starts_with(b.as_str()),
483 (Either2::B(a), Either2::B(b)) => {
484 if b.len() > a.len() {
485 return Ok(false);
486 } else if b.len() == a.len() {
487 return equals(s, &Val::Arr(a), &Val::Arr(b));
488 } else {
489 for (a, b) in a
490 .slice(None, Some(b.len()), None)
491 .iter(s.clone())
492 .zip(b.iter(s.clone()))
493 {
494 let a = a?;
495 let b = b?;
496 if !equals(s.clone(), &a, &b)? {
497 return Ok(false);
498 }
499 }
500 true
501 }
502 }
503 _ => throw_runtime!("both arguments should be of the same type"),
504 })
505}
506
507#[allow(clippy::comparison_chain)]
508#[builtin]
509fn builtin_ends_with(
510 s: State,
511 a: Either![IStr, ArrValue],
512 b: Either![IStr, ArrValue],
513) -> Result<bool> {
514 Ok(match (a, b) {
515 (Either2::A(a), Either2::A(b)) => a.ends_with(b.as_str()),
516 (Either2::B(a), Either2::B(b)) => {
517 if b.len() > a.len() {
518 return Ok(false);
519 } else if b.len() == a.len() {
520 return equals(s, &Val::Arr(a), &Val::Arr(b));
521 } else {
522 let a_len = a.len();
523 for (a, b) in a
524 .slice(Some(a_len - b.len()), None, None)
525 .iter(s.clone())
526 .zip(b.iter(s.clone()))
527 {
528 let a = a?;
529 let b = b?;
530 if !equals(s.clone(), &a, &b)? {
531 return Ok(false);
532 }
533 }
534 true
535 }
536 }
537 _ => throw_runtime!("both arguments should be of the same type"),
538 })
539}
540
541pub trait StateExt {
542 /// This method was previously implemented in jrsonnet-evaluator itself
543 fn with_stdlib(&self);
544 fn add_global(&self, name: IStr, value: Thunk<Val>);
545}
546
547impl StateExt for State {
548 fn with_stdlib(&self) {
549 let initializer = ContextInitializer::new(self.clone(), PathResolver::new_cwd_fallback());
550 self.settings_mut().context_initializer = Box::new(initializer)
551 }
552 fn add_global(&self, name: IStr, value: Thunk<Val>) {
553 self.settings()
554 .context_initializer
555 .as_any()
556 .downcast_ref::<ContextInitializer>()
557 .expect("not standard context initializer")
558 .settings_mut()
559 .globals
560 .insert(name, value);
561 }
562}
2563
addedcrates/jrsonnet-stdlib/src/manifest.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/math.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/parse.rsdiffbeforeafterboth

no changes

addedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth

no changes

modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
2 local std = self,2 local std = self,
3 local id = std.id,3 local id = std.id,
44
5 # Magic legacy field5 thisFile:: error 'std.thisFile is deprecated, to enable its support in jrsonnet - recompile it with "legacy-this-file" support.\nThis will slow down stdlib caching a bit, though',
6 thisFile:: $intrinsicThisFile,
7 id:: $intrinsicId,
86
9 # Those functions aren't normally located in stdlib7 toString(a):: '' + a,
10 length:: $intrinsic(length),
11 type:: $intrinsic(type),
12 makeArray:: $intrinsic(makeArray),
13 codepoint:: $intrinsic(codepoint),
14 objectFieldsEx:: $intrinsic(objectFieldsEx),
15 objectHasEx:: $intrinsic(objectHasEx),
16 primitiveEquals:: $intrinsic(primitiveEquals),
17 modulo:: $intrinsic(modulo),
18 floor:: $intrinsic(floor),
19 ceil:: $intrinsic(ceil),
20 extVar:: $intrinsic(extVar),
21 native:: $intrinsic(native),
22 filter:: $intrinsic(filter),
23 char:: $intrinsic(char),
24 encodeUTF8:: $intrinsic(encodeUTF8),
25 decodeUTF8:: $intrinsic(decodeUTF8),
26 md5:: $intrinsic(md5),
27 trace:: $intrinsic(trace),
28 parseJson:: $intrinsic(parseJson),
29 parseYaml:: $intrinsic(parseYaml),
308
31 log:: $intrinsic(log),
32 pow:: $intrinsic(pow),
33 sqrt:: $intrinsic(sqrt),
34
35 sin:: $intrinsic(sin),
36 cos:: $intrinsic(cos),
37 tan:: $intrinsic(tan),
38 asin:: $intrinsic(asin),
39 acos:: $intrinsic(acos),
40 atan:: $intrinsic(atan),
41
42 exp:: $intrinsic(exp),
43 mantissa:: $intrinsic(mantissa),
44 exponent:: $intrinsic(exponent),
45
46 any:: $intrinsic(any),
47 all:: $intrinsic(all),
48
49 isString(v):: std.type(v) == 'string',
50 isNumber(v):: std.type(v) == 'number',
51 isBoolean(v):: std.type(v) == 'boolean',
52 isObject(v):: std.type(v) == 'object',
53 isArray(v):: std.type(v) == 'array',
54 isFunction(v):: std.type(v) == 'function',
55
56 toString(a)::
57 if std.type(a) == 'string' then a else '' + a,
58
59 substr:: $intrinsic(substr),
60
61 startsWith(a, b)::
62 if std.length(a) < std.length(b) then
63 false
64 else
65 std.substr(a, 0, std.length(b)) == b,
66
67 endsWith(a, b)::
68 if std.length(a) < std.length(b) then
69 false
70 else
71 std.substr(a, std.length(a) - std.length(b), std.length(b)) == b,
72
73 lstripChars(str, chars)::9 lstripChars(str, chars)::
74 if std.length(str) > 0 && std.member(chars, str[0]) then10 if std.length(str) > 0 && std.member(chars, str[0]) then
75 std.lstripChars(str[1:], chars)11 std.lstripChars(str[1:], chars)
12763
128 split(str, c):: std.splitLimit(str, c, -1),64 split(str, c):: std.splitLimit(str, c, -1),
12965
130 splitLimit:: $intrinsic(splitLimit),
131
132 strReplace:: $intrinsic(strReplace),
133
134 asciiUpper:: $intrinsic(asciiUpper),
135
136 asciiLower:: $intrinsic(asciiLower),
137
138 range:: $intrinsic(range),
139
140 repeat(what, count)::66 repeat(what, count)::
141 local joiner =67 local joiner =
142 if std.isString(what) then ''68 if std.isString(what) then ''
143 else if std.isArray(what) then []69 else if std.isArray(what) then []
144 else error 'std.repeat first argument must be an array or a string';70 else error 'std.repeat first argument must be an array or a string';
145 std.join(joiner, std.makeArray(count, function(i) what)),71 std.join(joiner, std.makeArray(count, function(i) what)),
14672
147 slice:: $intrinsic(slice),
148
149 member:: $intrinsic(member),
150
151 count:: $intrinsic(count),
152
153 mod:: $intrinsic(mod),
154
155 map:: $intrinsic(map),
156
157 mapWithIndex(func, arr)::73 mapWithIndex(func, arr)::
158 if !std.isFunction(func) then74 if !std.isFunction(func) then
159 error ('std.mapWithIndex first param must be function, got ' + std.type(func))75 error ('std.mapWithIndex first param must be function, got ' + std.type(func))
170 else86 else
171 { [k]: func(k, obj[k]) for k in std.objectFields(obj) },87 { [k]: func(k, obj[k]) for k in std.objectFields(obj) },
17288
173 flatMap:: $intrinsic(flatMap),
174
175 join:: $intrinsic(join),
176
177 lines(arr)::89 lines(arr)::
178 std.join('\n', arr + ['']),90 std.join('\n', arr + ['']),
17991
185 else97 else
186 error 'Expected string or array, got %s' % std.type(arr),98 error 'Expected string or array, got %s' % std.type(arr),
18799
188
189 format:: $intrinsic(format),
190
191 foldr:: $intrinsic(foldr),
192
193 foldl:: $intrinsic(foldl),
194
195 filterMap(filter_func, map_func, arr)::100 filterMap(filter_func, map_func, arr)::
196 if !std.isFunction(filter_func) then101 if !std.isFunction(filter_func) then
197 error ('std.filterMap first param must be function, got ' + std.type(filter_func))102 error ('std.filterMap first param must be function, got ' + std.type(filter_func))
350 else255 else
351 error 'TOML body must be an object. Got ' + std.type(value),256 error 'TOML body must be an object. Got ' + std.type(value),
352257
353 escapeStringJson:: $intrinsic(escapeStringJson),
354
355 escapeStringPython(str)::258 escapeStringPython(str)::
356 std.escapeStringJson(str),259 std.escapeStringJson(str),
357260
377280
378 manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),281 manifestJsonMinified(value):: std.manifestJsonEx(value, '', '', ':'),
379282
380 manifestJsonEx:: $intrinsic(manifestJsonEx),
381
382 manifestYamlDoc:: $intrinsic(manifestYamlDoc),
383
384 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::283 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::
385 if !std.isArray(value) then284 if !std.isArray(value) then
386 error 'manifestYamlStream only takes arrays, got ' + std.type(value)285 error 'manifestYamlStream only takes arrays, got ' + std.type(value)
434333
435 aux(value),334 aux(value),
436335
437 local base64_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
438 local base64_inv = { [base64_table[i]]: i for i in std.range(0, 63) },
439
440 base64:: $intrinsic(base64),
441
442 base64DecodeBytes:: $intrinsic(base64DecodeBytes),
443
444 base64Decode:: $intrinsic(base64Decode),
445
446 reverse:: $intrinsic(reverse),
447
448 sort:: $intrinsic(sort),
449
450 uniq(arr, keyF=id)::336 uniq(arr, keyF=id)::
451 local f(a, b) =337 local f(a, b) =
452 if std.length(a) == 0 then338 if std.length(a) == 0 then
534 else420 else
535 patch,421 patch,
536422
537 get(o, f, default = null, inc_hidden = true)::423 get(o, f, default=null, inc_hidden=true)::
538 if std.objectHasEx(o, f, inc_hidden) then o[f] else default,424 if std.objectHasEx(o, f, inc_hidden) then o[f] else default,
539425
540 objectFields(o)::426 objectFields(o)::
555 objectValuesAll(o)::441 objectValuesAll(o)::
556 [o[k] for k in std.objectFieldsAll(o)],442 [o[k] for k in std.objectFieldsAll(o)],
557443
558 equals:: $intrinsic(equals),
559
560 resolvePath(f, r)::444 resolvePath(f, r)::
561 local arr = std.split(f, '/');445 local arr = std.split(f, '/');
562 std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),446 std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),
579 if isContent(std.prune(a[x]))463 if isContent(std.prune(a[x]))
580 } else464 } else
581 a,465 a,
582
583 findSubstr(pat, str)::
584 if !std.isString(pat) then
585 error 'findSubstr first parameter should be a string, got ' + std.type(pat)
586 else if !std.isString(str) then
587 error 'findSubstr second parameter should be a string, got ' + std.type(str)
588 else
589 local pat_len = std.length(pat);
590 local str_len = std.length(str);
591 if pat_len == 0 || str_len == 0 || pat_len > str_len then
592 []
593 else
594 std.filter(function(i) str[i:i + pat_len] == pat, std.range(0, str_len - pat_len)),
595466
596 find(value, arr)::467 find(value, arr)::
597 if !std.isArray(arr) then468 if !std.isArray(arr) then
addedcrates/jrsonnet-stdlib/src/types.rsdiffbeforeafterboth

no changes

deletedflake.lockdiffbeforeafterboth

no changes

deletedflake.nixdiffbeforeafterboth

no changes

addedtests/Cargo.tomldiffbeforeafterboth

no changes

addedtests/golden/array_comp.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/array_comp.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/builtin_json.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/builtin_json.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/builtin_json_minified.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/builtin_json_minified.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/builtin_parseJson.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/builtin_parseJson.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/issue23.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/issue23.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/issue40.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/issue40.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/missing_binding.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/missing_binding.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/object_comp.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/object_comp.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/golden/test_assertThrow.jsonnetdiffbeforeafterboth

no changes

addedtests/golden/test_assertThrow.jsonnet.goldendiffbeforeafterboth

no changes

addedtests/src/lib.rsdiffbeforeafterboth

no changes

addedtests/suite/builtin_ascii.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/builtin_base64.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/builtin_chars.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/builtin_constant.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/builtin_count.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/builtin_join.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/builtin_member.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/function_args.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/function_context.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/function_lazy_args.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/local.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/math.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/object_assertion.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/object_comp_self.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/object_context.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/object_fields.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/object_inheritance.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/object_locals.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/object_super_standalone.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/sjsonnet_issue_127.jsonnetdiffbeforeafterboth

no changes

addedtests/suite/string_concat.jsonnetdiffbeforeafterboth

no changes

addedtests/tests/as_native.rsdiffbeforeafterboth

no changes

addedtests/tests/builtin.rsdiffbeforeafterboth

no changes

addedtests/tests/common.rsdiffbeforeafterboth

no changes

addedtests/tests/golden.rsdiffbeforeafterboth

no changes

addedtests/tests/sanity.rsdiffbeforeafterboth

no changes

addedtests/tests/suite.rsdiffbeforeafterboth

no changes

addedtests/tests/typed_obj.rsdiffbeforeafterboth

no changes