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,17};18use jrsonnet_gcmodule::Trace;19use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};2021use crate::VM;2223pub type JsonnetImportCallback = unsafe extern "C" fn(24 ctx: *mut c_void,25 base: *const c_char,26 rel: *const c_char,27 found_here: *mut *const c_char,28 success: &mut c_int,29) -> *mut c_char;303132#[derive(Trace)]33pub struct CallbackImportResolver {34 #[trace(skip)]35 cb: JsonnetImportCallback,36 #[trace(skip)]37 ctx: *mut c_void,38 out: RefCell<HashMap<SourcePath, Vec<u8>>>,39}40impl ImportResolver for CallbackImportResolver {41 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {42 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {43 let mut o = p.path().to_owned();44 o.pop();45 o46 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {47 d.path().to_owned()48 } else if from.is_default() {49 current_dir().map_err(|e| ImportIo(e.to_string()))?50 } else {51 unreachable!("can't resolve this path");52 };53 let base = unsafe { crate::unparse_path(&base) };54 let rel = CString::new(path).unwrap();55 let found_here: *mut c_char = null_mut();56 let mut success: i32 = 0;57 let result_ptr = unsafe {58 (self.cb)(59 self.ctx,60 base.as_ptr(),61 rel.as_ptr(),62 &mut (found_here as *const _),63 &mut success,64 )65 };66 let result_raw = unsafe { CStr::from_ptr(result_ptr) };67 let result_str = result_raw.to_str().unwrap();68 assert!(success == 0 || success == 1);69 if success == 0 {70 unsafe { CString::from_raw(result_ptr) };71 let result = result_str.to_owned();72 throw!(ImportCallbackError(result));73 }7475 let found_here_raw = unsafe { CStr::from_ptr(found_here) };76 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(77 found_here_raw.to_str().unwrap(),78 )));79 unsafe {80 let _ = CString::from_raw(found_here);81 }8283 let mut out = self.out.borrow_mut();84 if !out.contains_key(&found_here_buf) {85 out.insert(found_here_buf.clone(), result_str.into());86 unsafe { CString::from_raw(result_ptr) };87 }8889 Ok(found_here_buf)90 }91 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {92 Ok(self.out.borrow().get(resolved).unwrap().clone())93 }9495 fn as_any(&self) -> &dyn Any {96 self97 }98}99100101102103#[no_mangle]104pub unsafe extern "C" fn jsonnet_import_callback(105 vm: &VM,106 cb: JsonnetImportCallback,107 ctx: *mut c_void,108) {109 vm.state110 .set_import_resolver(Box::new(CallbackImportResolver {111 cb,112 ctx,113 out: RefCell::new(HashMap::new()),114 }))115}116117118119120#[no_mangle]121pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {122 let cstr = CStr::from_ptr(path);123 let path = PathBuf::from(cstr.to_str().unwrap());124 let any_resolver = vm.state.import_resolver();125 let resolver = any_resolver126 .as_any()127 .downcast_ref::<FileImportResolver>()128 .expect("jpaths are not compatible with callback imports!");129 resolver.add_jpath(path);130}