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.state120 .set_import_resolver(Box::new(CallbackImportResolver {121 cb,122 ctx,123 out: RefCell::new(HashMap::new()),124 }))125}126127128129130#[no_mangle]131pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {132 let cstr = CStr::from_ptr(path);133 let path = PathBuf::from(cstr.to_str().unwrap());134 let any_resolver = vm.state.import_resolver();135 let resolver = any_resolver136 .as_any()137 .downcast_ref::<FileImportResolver>()138 .expect("jpaths are not compatible with callback imports!");139 resolver.add_jpath(path);140}