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_gcmodule::Trace;19use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};2021pub type JsonnetImportCallback = unsafe extern "C" fn(22 ctx: *mut c_void,23 base: *const c_char,24 rel: *const c_char,25 found_here: *mut *const c_char,26 success: &mut c_int,27) -> *mut c_char;282930#[derive(Trace)]31pub struct CallbackImportResolver {32 #[trace(skip)]33 cb: JsonnetImportCallback,34 #[trace(skip)]35 ctx: *mut c_void,36 out: RefCell<HashMap<SourcePath, Vec<u8>>>,37}38impl ImportResolver for CallbackImportResolver {39 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {40 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {41 let mut o = p.path().to_owned();42 o.pop();43 o44 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {45 d.path().to_owned()46 } else if from.is_default() {47 current_dir().map_err(|e| ImportIo(e.to_string()))?48 } else {49 unreachable!("can't resolve this path");50 };51 let base = unsafe { crate::unparse_path(&base) };52 let rel = CString::new(path).unwrap();53 let found_here: *mut c_char = null_mut();54 let mut success: i32 = 0;55 let result_ptr = unsafe {56 (self.cb)(57 self.ctx,58 base.as_ptr(),59 rel.as_ptr(),60 &mut (found_here as *const _),61 &mut success,62 )63 };64 let result_raw = unsafe { CStr::from_ptr(result_ptr) };65 let result_str = result_raw.to_str().unwrap();66 assert!(success == 0 || success == 1);67 if success == 0 {68 unsafe { CString::from_raw(result_ptr) };69 let result = result_str.to_owned();70 throw!(ImportCallbackError(result));71 }7273 let found_here_raw = unsafe { CStr::from_ptr(found_here) };74 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(75 found_here_raw.to_str().unwrap(),76 )));77 unsafe {78 let _ = CString::from_raw(found_here);79 }8081 let mut out = self.out.borrow_mut();82 if !out.contains_key(&found_here_buf) {83 out.insert(found_here_buf.clone(), result_str.into());84 unsafe { CString::from_raw(result_ptr) };85 }8687 Ok(found_here_buf)88 }89 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {90 Ok(self.out.borrow().get(resolved).unwrap().clone())91 }9293 fn as_any(&self) -> &dyn Any {94 self95 }96}979899100101#[no_mangle]102pub unsafe extern "C" fn jsonnet_import_callback(103 vm: &State,104 cb: JsonnetImportCallback,105 ctx: *mut c_void,106) {107 vm.set_import_resolver(Box::new(CallbackImportResolver {108 cb,109 ctx,110 out: RefCell::new(HashMap::new()),111 }))112}113114115116117#[no_mangle]118pub unsafe extern "C" fn jsonnet_jpath_add(vm: &State, path: *const c_char) {119 let cstr = CStr::from_ptr(path);120 let path = PathBuf::from(cstr.to_str().unwrap());121 let any_resolver = vm.import_resolver();122 let resolver = any_resolver123 .as_any()124 .downcast_ref::<FileImportResolver>()125 .expect("jpaths are not compatible with callback imports!");126 resolver.add_jpath(path);127}