123use std::{4 any::Any,5 cell::RefCell,6 collections::HashMap,7 env::current_dir,8 ffi::{c_void, CStr, CString},9 os::raw::{c_char, c_int},10 path::PathBuf,11 ptr::null_mut,12};1314use jrsonnet_evaluator::{15 error::{Error::*, Result},16 throw, FileImportResolver, ImportResolver, State,17};18use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};1920pub type JsonnetImportCallback = unsafe extern "C" fn(21 ctx: *mut c_void,22 base: *const c_char,23 rel: *const c_char,24 found_here: *mut *const c_char,25 success: &mut c_int,26) -> *mut c_char;272829pub struct CallbackImportResolver {30 cb: JsonnetImportCallback,31 ctx: *mut c_void,32 out: RefCell<HashMap<SourcePath, Vec<u8>>>,33}34impl ImportResolver for CallbackImportResolver {35 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {36 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {37 let mut o = p.path().to_owned();38 o.pop();39 o40 } 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) };48 let rel = CString::new(path).unwrap();49 let found_here: *mut c_char = null_mut();50 let mut success: i32 = 0;51 let result_ptr = unsafe {52 (self.cb)(53 self.ctx,54 base.as_ptr(),55 rel.as_ptr(),56 &mut (found_here as *const _),57 &mut success,58 )59 };60 let result_raw = unsafe { CStr::from_ptr(result_ptr) };61 let result_str = result_raw.to_str().unwrap();62 assert!(success == 0 || success == 1);63 if success == 0 {64 unsafe { CString::from_raw(result_ptr) };65 let result = result_str.to_owned();66 throw!(ImportCallbackError(result));67 }6869 let found_here_raw = unsafe { CStr::from_ptr(found_here) };70 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(71 found_here_raw.to_str().unwrap(),72 )));73 unsafe {74 let _ = CString::from_raw(found_here);75 }7677 let mut out = self.out.borrow_mut();78 if !out.contains_key(&found_here_buf) {79 out.insert(found_here_buf.clone(), result_str.into());80 unsafe { CString::from_raw(result_ptr) };81 }8283 Ok(found_here_buf)84 }85 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {86 Ok(self.out.borrow().get(resolved).unwrap().clone())87 }8889 fn as_any(&self) -> &dyn Any {90 self91 }92}9394959697#[no_mangle]98pub unsafe extern "C" fn jsonnet_import_callback(99 vm: &State,100 cb: JsonnetImportCallback,101 ctx: *mut c_void,102) {103 vm.set_import_resolver(Box::new(CallbackImportResolver {104 cb,105 ctx,106 out: RefCell::new(HashMap::new()),107 }))108}109110111112113#[no_mangle]114pub unsafe extern "C" fn jsonnet_jpath_add(vm: &State, v: *const c_char) {115 let cstr = CStr::from_ptr(v);116 let path = PathBuf::from(cstr.to_str().unwrap());117 let any_resolver = vm.import_resolver();118 let resolver = any_resolver119 .as_any()120 .downcast_ref::<FileImportResolver>()121 .expect("jpaths are not compatible with callback imports!");122 resolver.add_jpath(path);123}