123use std::{4 alloc::Layout,5 any::Any,6 cell::RefCell,7 collections::HashMap,8 env::current_dir,9 ffi::{c_void, CStr, CString},10 os::raw::{c_char, c_int},11 path::PathBuf,12 ptr::null_mut,13};1415use jrsonnet_evaluator::{16 error::{ErrorKind::*, Result},17 throw, FileImportResolver, ImportResolver,18};19use jrsonnet_gcmodule::Trace;20use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};2122use crate::VM;2324pub type JsonnetImportCallback = unsafe extern "C" fn(25 ctx: *mut c_void,26 base: *const c_char,27 rel: *const c_char,28 found_here: *mut *const c_char,29 buf: *mut *mut c_char,30 buflen: *mut usize,31) -> c_int;323334#[derive(Trace)]35pub struct CallbackImportResolver {36 #[trace(skip)]37 cb: JsonnetImportCallback,38 #[trace(skip)]39 ctx: *mut c_void,40 out: RefCell<HashMap<SourcePath, Vec<u8>>>,41}42impl ImportResolver for CallbackImportResolver {43 fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {44 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {45 let mut o = p.path().to_owned();46 o.pop();47 o48 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {49 d.path().to_owned()50 } else if from.is_default() {51 current_dir().map_err(|e| ImportIo(e.to_string()))?52 } else {53 unreachable!("can't resolve this path");54 };55 let base = unsafe { crate::unparse_path(&base) };56 let rel = CString::new(path).unwrap();57 let found_here: *mut c_char = null_mut();5859 let mut buf = null_mut();60 let mut buf_len = 0;61 let success = unsafe {62 (self.cb)(63 self.ctx,64 base.as_ptr(),65 rel.as_ptr(),66 &mut (found_here as *const _),67 &mut buf,68 &mut buf_len,69 )70 };71 let buf_slice: &[u8] = unsafe { std::slice::from_raw_parts(buf.cast(), buf_len) };72 unsafe {73 std::alloc::dealloc(74 buf.cast(),75 Layout::from_size_align(buf_len, 1).expect("layout is valid"),76 );77 };78 let buf_intern = buf_slice.to_vec();7980 assert!(success == 0 || success == 1);81 if success == 0 {82 let result = String::from_utf8(buf_intern).expect("error should be valid string");83 throw!(ImportCallbackError(result));84 }8586 let found_here_raw = unsafe { CStr::from_ptr(found_here) };87 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(88 found_here_raw.to_str().unwrap(),89 )));90 unsafe {91 let _ = CString::from_raw(found_here);92 }9394 let mut out = self.out.borrow_mut();95 if !out.contains_key(&found_here_buf) {96 out.insert(found_here_buf.clone(), buf_intern);97 }9899 Ok(found_here_buf)100 }101 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {102 Ok(self.out.borrow().get(resolved).unwrap().clone())103 }104105 fn as_any(&self) -> &dyn Any {106 self107 }108}109110111112113#[no_mangle]114pub unsafe extern "C" fn jsonnet_import_callback(115 vm: &VM,116 cb: JsonnetImportCallback,117 ctx: *mut c_void,118) {119 vm.state.set_import_resolver(CallbackImportResolver {120 cb,121 ctx,122 out: RefCell::new(HashMap::new()),123 })124}125126127128129#[no_mangle]130pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {131 let cstr = CStr::from_ptr(path);132 let path = PathBuf::from(cstr.to_str().unwrap());133 let any_resolver = vm.state.import_resolver();134 let resolver = any_resolver135 .as_any()136 .downcast_ref::<FileImportResolver>()137 .expect("jpaths are not compatible with callback imports!");138 resolver.add_jpath(path);139}