1#![deny(missing_docs)]
22#![allow(trivial_casts)]
23
24use libc::{c_char, c_int, c_uint, c_void};
25use std::ffi::{CStr, CString, OsStr};
26use std::ops::Deref;
27use std::path::{Path, PathBuf};
28use std::{error, fmt, iter, ptr};
29
30mod string;
31mod value;
32
33pub use crate::string::JsonnetString;
34use crate::string::JsonnetStringIter;
35pub use crate::value::{JsonVal, JsonValue};
36use jsonnet_sys::JsonnetJsonValue;
37
38#[derive(Debug, PartialEq, Eq)]
40pub struct Error<'a>(JsonnetString<'a>);
41
42impl<'a> From<JsonnetString<'a>> for Error<'a> {
43 fn from(s: JsonnetString<'a>) -> Self {
44 Error(s)
45 }
46}
47
48impl<'a> From<Error<'a>> for JsonnetString<'a> {
49 fn from(e: Error<'a>) -> Self {
50 e.0
51 }
52}
53
54impl<'a> Deref for Error<'a> {
55 type Target = JsonnetString<'a>;
56 fn deref(&self) -> &Self::Target {
57 &self.0
58 }
59}
60
61impl<'a> fmt::Display for Error<'a> {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 fmt::Display::fmt(&self.0, f)
64 }
65}
66
67impl<'a> error::Error for Error<'a> {
68 fn description(&self) -> &str {
69 "jsonnet error"
70 }
71}
72
73pub struct EvalMap<'a>(JsonnetString<'a>);
77impl<'a> EvalMap<'a> {
78 pub fn iter<'b>(&'b self) -> EvalMapIter<'b, 'a> {
80 EvalMapIter(unsafe { JsonnetStringIter::new(&self.0) })
81 }
82}
83
84impl<'a, 'b> IntoIterator for &'b EvalMap<'a> {
85 type Item = (&'b str, &'b str);
86 type IntoIter = EvalMapIter<'b, 'a>;
87 fn into_iter(self) -> Self::IntoIter {
88 self.iter()
89 }
90}
91
92#[derive(Debug)]
96pub struct EvalMapIter<'a, 'b: 'a>(JsonnetStringIter<'a, 'b>);
97
98impl<'a, 'b> Iterator for EvalMapIter<'a, 'b> {
99 type Item = (&'a str, &'a str);
100 fn next(&mut self) -> Option<Self::Item> {
101 self.0.next().map(|first| {
102 let second = self.0.next().unwrap();
103 (first, second)
104 })
105 }
106}
107
108pub struct EvalList<'a>(JsonnetString<'a>);
112impl<'a> EvalList<'a> {
113 pub fn iter<'b>(&'b self) -> EvalListIter<'b, 'a> {
115 EvalListIter(unsafe { JsonnetStringIter::new(&self.0) })
116 }
117}
118
119pub struct EvalListIter<'a, 'b: 'a>(JsonnetStringIter<'a, 'b>);
123
124impl<'a, 'b> Iterator for EvalListIter<'a, 'b> {
125 type Item = &'a str;
126 fn next(&mut self) -> Option<Self::Item> {
127 self.0.next()
128 }
129}
130
131impl<'a, 'b> IntoIterator for &'b EvalList<'a> {
132 type Item = &'b str;
133 type IntoIter = EvalListIter<'b, 'a>;
134 fn into_iter(self) -> Self::IntoIter {
135 self.iter()
136 }
137}
138
139#[derive(Debug, PartialEq, Eq)]
141pub enum FmtString {
142 Double,
144 Single,
146 Leave,
148}
149
150impl FmtString {
151 fn to_char(self) -> c_int {
152 let c = match self {
153 FmtString::Double => 'd',
154 FmtString::Single => 's',
155 FmtString::Leave => 'l',
156 };
157 c as c_int
158 }
159}
160
161impl Default for FmtString {
162 fn default() -> FmtString {
163 FmtString::Leave
164 }
165}
166
167#[derive(Debug, PartialEq, Eq)]
169pub enum FmtComment {
170 Hash,
172 Slash,
174 Leave,
176}
177
178impl FmtComment {
179 fn to_char(self) -> c_int {
180 let c = match self {
181 FmtComment::Hash => 'h',
182 FmtComment::Slash => 's',
183 FmtComment::Leave => 'l',
184 };
185 c as c_int
186 }
187}
188
189impl Default for FmtComment {
190 fn default() -> FmtComment {
191 FmtComment::Leave
192 }
193}
194
195pub fn jsonnet_version() -> &'static str {
200 let s = unsafe { CStr::from_ptr(jsonnet_sys::jsonnet_version()) };
201 s.to_str().unwrap()
202}
203
204#[test]
205fn test_version() {
206 let s = jsonnet_version();
207 println!("Got version: {}", s);
208 assert_eq!(s.split('.').count(), 3);
210}
211
212struct ImportContext<'a, F> {
214 vm: &'a JsonnetVm,
215 cb: F,
216}
217
218#[cfg(unix)]
219fn bytes2osstr(b: &[u8]) -> &OsStr {
220 use std::os::unix::ffi::OsStrExt;
221 OsStr::from_bytes(b)
222}
223
224#[cfg(windows)]
226fn bytes2osstr(b: &[u8]) -> &OsStr {
227 let s = std::str::from_utf8(b).unwrap();
228 OsStr::new(s)
229}
230
231fn cstr2osstr(cstr: &CStr) -> &OsStr {
232 bytes2osstr(cstr.to_bytes())
233}
234
235#[cfg(unix)]
236fn osstr2bytes(os: &OsStr) -> &[u8] {
237 use std::os::unix::ffi::OsStrExt;
238 os.as_bytes()
239}
240
241#[cfg(windows)]
243fn osstr2bytes(os: &OsStr) -> &[u8] {
244 os.to_str().unwrap().as_bytes()
245}
246
247fn osstr2cstring<T: AsRef<OsStr>>(os: T) -> CString {
248 let b = osstr2bytes(os.as_ref());
249 CString::new(b).unwrap()
250}
251
252extern "C" fn import_callback<F>(
256 ctx: *mut c_void,
257 base: &c_char,
258 rel: &c_char,
259 found_here: &mut *mut c_char,
260 success: &mut c_int,
261) -> *mut c_char
262where
263 F: Fn(&JsonnetVm, &Path, &Path) -> Result<(PathBuf, String), String>,
264{
265 let ctx = unsafe { &*(ctx as *mut ImportContext<F>) };
266 let vm = ctx.vm;
267 let callback = &ctx.cb;
268 let base_path = Path::new(cstr2osstr(unsafe { CStr::from_ptr(base) }));
269 let rel_path = Path::new(cstr2osstr(unsafe { CStr::from_ptr(rel) }));
270 match callback(vm, base_path, rel_path) {
271 Ok((found_here_path, contents)) => {
272 *success = 1;
273
274 let v = {
275 let b = osstr2bytes(found_here_path.as_os_str());
277 JsonnetString::from_bytes(vm, b)
278 };
279 *found_here = v.into_raw();
280
281 JsonnetString::new(vm, &contents).into_raw()
282 }
283 Err(err) => {
284 *success = 0;
285 JsonnetString::new(vm, &err).into_raw()
286 }
287 }
288}
289
290struct NativeContext<'a, F> {
296 vm: &'a JsonnetVm,
297 argc: usize,
298 cb: F,
299}
300
301extern "C" fn native_callback<'a, F>(
305 ctx: *mut libc::c_void,
306 argv: *const *const JsonnetJsonValue,
307 success: &mut c_int,
308) -> *mut JsonnetJsonValue
309where
310 F: Fn(&'a JsonnetVm, &[JsonVal<'a>]) -> Result<JsonValue<'a>, String>,
311{
312 let ctx = unsafe { &*(ctx as *mut NativeContext<F>) };
313 let vm = ctx.vm;
314 let callback = &ctx.cb;
315 let args: Vec<_> = (0..ctx.argc)
316 .map(|i| unsafe { JsonVal::from_ptr(vm, *argv.offset(i as isize)) })
317 .collect();
318 match callback(vm, &args) {
319 Ok(v) => {
320 *success = 1;
321 v.into_raw()
322 }
323 Err(err) => {
324 *success = 0;
325 let ret = JsonValue::from_str(vm, &err);
326 ret.into_raw()
327 }
328 }
329}
330
331pub struct JsonnetVm(*mut jsonnet_sys::JsonnetVm);
333
334impl JsonnetVm {
335 pub fn new() -> Self {
337 let vm = unsafe { jsonnet_sys::jsonnet_make() };
338 JsonnetVm(vm)
339 }
340
341 fn as_ptr(&self) -> *mut jsonnet_sys::JsonnetVm {
342 self.0
343 }
344
345 pub fn max_stack(&mut self, v: u32) {
347 unsafe { jsonnet_sys::jsonnet_max_stack(self.0, v as c_uint) }
348 }
349
350 pub fn gc_min_objects(&mut self, v: u32) {
353 unsafe { jsonnet_sys::jsonnet_gc_min_objects(self.0, v as c_uint) }
354 }
355
356 pub fn gc_growth_trigger(&mut self, v: f64) {
359 unsafe { jsonnet_sys::jsonnet_gc_growth_trigger(self.0, v) }
360 }
361
362 pub fn string_output(&mut self, v: bool) {
364 unsafe { jsonnet_sys::jsonnet_string_output(self.0, v as c_int) }
365 }
366
367 pub fn import_callback<F>(&mut self, cb: F)
398 where
399 F: Fn(&JsonnetVm, &Path, &Path) -> Result<(PathBuf, String), String>,
400 {
401 let ctx = ImportContext { vm: self, cb: cb };
402 unsafe {
403 jsonnet_sys::jsonnet_import_callback(
404 self.as_ptr(),
405 import_callback::<F> as *const _,
406 Box::into_raw(Box::new(ctx)) as *mut _,
408 );
409 }
410 }
411
412 pub fn native_callback<F>(&mut self, name: &str, cb: F, params: &[&str])
458 where
459 for<'a> F: Fn(&'a JsonnetVm, &[JsonVal<'a>]) -> Result<JsonValue<'a>, String>,
460 {
461 let ctx = NativeContext {
462 vm: self,
463 argc: params.len(),
464 cb: cb,
465 };
466 let cname = CString::new(name).unwrap();
467 let cparams: Vec<_> = params
468 .into_iter()
469 .map(|&p| CString::new(p).unwrap())
470 .collect();
471 let cptrs: Vec<_> = cparams
472 .iter()
473 .map(|p| p.as_ptr())
474 .chain(iter::once(ptr::null()))
475 .collect();
476 unsafe {
477 jsonnet_sys::jsonnet_native_callback(
478 self.as_ptr(),
479 cname.as_ptr(),
480 native_callback::<F> as *const _,
481 Box::into_raw(Box::new(ctx)) as *mut _,
483 cptrs.as_slice().as_ptr(),
484 );
485 }
486 }
487
488 pub fn ext_var(&mut self, key: &str, val: &str) {
494 let ckey = CString::new(key).unwrap();
495 let cval = CString::new(val).unwrap();
496 unsafe {
497 jsonnet_sys::jsonnet_ext_var(self.0, ckey.as_ptr(), cval.as_ptr());
498 }
499 }
500
501 pub fn ext_code(&mut self, key: &str, code: &str) {
507 let ckey = CString::new(key).unwrap();
508 let ccode = CString::new(code).unwrap();
509 unsafe {
510 jsonnet_sys::jsonnet_ext_code(self.0, ckey.as_ptr(), ccode.as_ptr());
511 }
512 }
513
514 pub fn tla_var(&mut self, key: &str, val: &str) {
520 let ckey = CString::new(key).unwrap();
521 let cval = CString::new(val).unwrap();
522 unsafe {
523 jsonnet_sys::jsonnet_tla_var(self.0, ckey.as_ptr(), cval.as_ptr());
524 }
525 }
526
527 pub fn tla_code(&mut self, key: &str, code: &str) {
533 let ckey = CString::new(key).unwrap();
534 let ccode = CString::new(code).unwrap();
535 unsafe {
536 jsonnet_sys::jsonnet_tla_code(self.0, ckey.as_ptr(), ccode.as_ptr());
537 }
538 }
539
540 pub fn fmt_indent(&mut self, n: u32) {
542 unsafe {
543 jsonnet_sys::jsonnet_fmt_indent(self.0, n as c_int);
544 }
545 }
546
547 pub fn fmt_max_blank_lines(&mut self, n: u32) {
549 unsafe {
550 jsonnet_sys::jsonnet_fmt_max_blank_lines(self.0, n as c_int);
551 }
552 }
553
554 pub fn fmt_string(&mut self, fmt: FmtString) {
556 unsafe {
557 jsonnet_sys::jsonnet_fmt_string(self.0, fmt.to_char());
558 }
559 }
560
561 pub fn fmt_comment(&mut self, fmt: FmtComment) {
563 unsafe {
564 jsonnet_sys::jsonnet_fmt_comment(self.0, fmt.to_char());
565 }
566 }
567
568 pub fn fmt_pad_arrays(&mut self, pad: bool) {
570 unsafe {
571 jsonnet_sys::jsonnet_fmt_pad_arrays(self.0, pad as c_int);
572 }
573 }
574
575 pub fn fmt_pad_objects(&mut self, pad: bool) {
577 unsafe {
578 jsonnet_sys::jsonnet_fmt_pad_objects(self.0, pad as c_int);
579 }
580 }
581
582 pub fn fmt_pretty_field_names(&mut self, sugar: bool) {
584 unsafe {
585 jsonnet_sys::jsonnet_fmt_pretty_field_names(self.0, sugar as c_int);
586 }
587 }
588
589 pub fn fmt_sort_import(&mut self, sort: bool) {
591 unsafe {
592 jsonnet_sys::jsonnet_fmt_sort_imports(self.0, sort as c_int);
593 }
594 }
595
596 pub fn fmt_debug_desugaring(&mut self, reformat: bool) {
598 unsafe {
599 jsonnet_sys::jsonnet_fmt_debug_desugaring(self.0, reformat as c_int);
600 }
601 }
602
603 pub fn fmt_file<'a, P>(&'a mut self, filename: P) -> Result<JsonnetString<'a>, Error<'a>>
609 where
610 P: AsRef<OsStr>,
611 {
612 let fname = osstr2cstring(filename);
613 let mut error = 1;
614 let output = unsafe {
615 let v = jsonnet_sys::jsonnet_fmt_file(self.0, fname.as_ptr(), &mut error);
616 JsonnetString::from_ptr(self, v)
617 };
618 match error {
619 0 => Ok(output),
620 _ => Err(Error(output)),
621 }
622 }
623
624 pub fn fmt_snippet<'a, P>(
630 &'a mut self,
631 filename: P,
632 snippet: &str,
633 ) -> Result<JsonnetString<'a>, Error<'a>>
634 where
635 P: AsRef<OsStr>,
636 {
637 let fname = osstr2cstring(filename);
638 let snippet = CString::new(snippet).unwrap();
639 let mut error = 1;
640 let output = unsafe {
641 let v = jsonnet_sys::jsonnet_fmt_snippet(
642 self.0,
643 fname.as_ptr(),
644 snippet.as_ptr(),
645 &mut error,
646 );
647 JsonnetString::from_ptr(self, v)
648 };
649 match error {
650 0 => Ok(output),
651 _ => Err(Error(output)),
652 }
653 }
654
655 pub fn max_trace(&mut self, limit: Option<u32>) {
657 let v = limit.unwrap_or(0);
658 unsafe {
659 jsonnet_sys::jsonnet_max_trace(self.0, v);
660 }
661 }
662
663 pub fn jpath_add<P>(&mut self, path: P)
672 where
673 P: AsRef<OsStr>,
674 {
675 let v = osstr2cstring(path);
676 unsafe {
677 jsonnet_sys::jsonnet_jpath_add(self.0, v.as_ptr());
678 }
679 }
680
681 pub fn evaluate_file<'a, P>(&'a mut self, filename: P) -> Result<JsonnetString<'a>, Error<'a>>
691 where
692 P: AsRef<OsStr>,
693 {
694 let fname = osstr2cstring(filename);
695 let mut error = 1;
696 let output = unsafe {
697 let v = jsonnet_sys::jsonnet_evaluate_file(self.0, fname.as_ptr(), &mut error);
698 JsonnetString::from_ptr(self, v)
699 };
700 match error {
701 0 => Ok(output),
702 _ => Err(Error(output)),
703 }
704 }
705
706 pub fn evaluate_snippet<'a, P>(
718 &'a mut self,
719 filename: P,
720 snippet: &str,
721 ) -> Result<JsonnetString<'a>, Error<'a>>
722 where
723 P: AsRef<OsStr>,
724 {
725 let fname = osstr2cstring(filename);
726 let snip = CString::new(snippet).unwrap();
727 let mut error = 1;
728 let output = unsafe {
729 let v = jsonnet_sys::jsonnet_evaluate_snippet(
730 self.0,
731 fname.as_ptr(),
732 snip.as_ptr(),
733 &mut error,
734 );
735 JsonnetString::from_ptr(self, v)
736 };
737 match error {
738 0 => Ok(output),
739 _ => Err(Error(output)),
740 }
741 }
742
743 pub fn evaluate_file_multi<'a, P>(&'a mut self, filename: P) -> Result<EvalMap<'a>, Error<'a>>
754 where
755 P: AsRef<OsStr>,
756 {
757 let fname = osstr2cstring(filename);
758 let mut error = 1;
759 let output = unsafe {
760 let v = jsonnet_sys::jsonnet_evaluate_file_multi(self.0, fname.as_ptr(), &mut error);
761 JsonnetString::from_ptr(self, v)
762 };
763 match error {
764 0 => Ok(EvalMap(output)),
765 _ => Err(Error(output)),
766 }
767 }
768
769 pub fn evaluate_snippet_multi<'a, P>(
794 &'a mut self,
795 filename: P,
796 snippet: &str,
797 ) -> Result<EvalMap<'a>, Error<'a>>
798 where
799 P: AsRef<OsStr>,
800 {
801 let fname = osstr2cstring(filename);
802 let snippet = CString::new(snippet).unwrap();
803 let mut error = 1;
804 let output = unsafe {
805 let v = jsonnet_sys::jsonnet_evaluate_snippet_multi(
806 self.0,
807 fname.as_ptr(),
808 snippet.as_ptr(),
809 &mut error,
810 );
811 JsonnetString::from_ptr(self, v)
812 };
813 match error {
814 0 => Ok(EvalMap(output)),
815 _ => Err(Error(output)),
816 }
817 }
818
819 pub fn evaluate_file_stream<'a, P>(&'a mut self, filename: P) -> Result<EvalList<'a>, Error<'a>>
830 where
831 P: AsRef<OsStr>,
832 {
833 let fname = osstr2cstring(filename);
834 let mut error = 1;
835 let output = unsafe {
836 let v = jsonnet_sys::jsonnet_evaluate_file_stream(self.0, fname.as_ptr(), &mut error);
837 JsonnetString::from_ptr(self, v)
838 };
839 match error {
840 0 => Ok(EvalList(output)),
841 _ => Err(Error(output)),
842 }
843 }
844
845 pub fn evaluate_snippet_stream<'a, P>(
869 &'a mut self,
870 filename: P,
871 snippet: &str,
872 ) -> Result<EvalList<'a>, Error<'a>>
873 where
874 P: AsRef<OsStr>,
875 {
876 let fname = osstr2cstring(filename);
877 let snippet = CString::new(snippet).unwrap();
878 let mut error = 1;
879 let output = unsafe {
880 let v = jsonnet_sys::jsonnet_evaluate_snippet_stream(
881 self.0,
882 fname.as_ptr(),
883 snippet.as_ptr(),
884 &mut error,
885 );
886 JsonnetString::from_ptr(self, v)
887 };
888 match error {
889 0 => Ok(EvalList(output)),
890 _ => Err(Error(output)),
891 }
892 }
893}
894
895impl Drop for JsonnetVm {
896 fn drop(&mut self) {
897 assert!(!self.0.is_null());
898 unsafe { jsonnet_sys::jsonnet_destroy(self.0) }
899 }
900}
901
902#[test]
903fn basic_eval() {
904 let mut vm = JsonnetVm::new();
905 let result = vm.evaluate_snippet("example", "'Hello ' + 'World'");
906 println!("result is {:?}", result);
907 assert!(result.is_ok());
908 assert_eq!(result.unwrap().to_string(), "\"Hello World\"\n");
909}
910
911#[test]
912fn basic_eval_err() {
913 let mut vm = JsonnetVm::new();
914 let result = vm.evaluate_snippet("example", "bogus");
915 println!("result is {:?}", result);
916 assert!(result.is_err());
917 assert!(result.unwrap_err().contains("Unknown variable: bogus"));
918}