123use std::{4 alloc::Layout,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 bail,16 error::{ErrorKind::*, Result},17 AsPathLike, ImportResolver, ResolvePath,18};19use jrsonnet_gcmodule::Acyclic;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(Acyclic)]35pub struct CallbackImportResolver {36 cb: JsonnetImportCallback,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: &dyn AsPathLike) -> 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 = path.as_path();55 let rel = match rel {56 ResolvePath::Str(s) => CString::new(s.as_bytes()).unwrap(),57 ResolvePath::Path(p) => unsafe { crate::unparse_path(p) },58 };59 let found_here: *mut c_char = null_mut();6061 let mut buf = null_mut();62 let mut buf_len = 0;63 let success = unsafe {64 (self.cb)(65 self.ctx,66 base.as_ptr(),67 rel.as_ptr(),68 &mut found_here.cast_const(),69 &mut buf,70 &mut buf_len,71 )72 };73 let buf_slice: &[u8] = unsafe { std::slice::from_raw_parts(buf.cast(), buf_len) };74 unsafe {75 std::alloc::dealloc(76 buf.cast(),77 Layout::from_size_align(buf_len, 1).expect("layout is valid"),78 );79 };80 let buf_intern = buf_slice.to_vec();8182 assert!(success == 0 || success == 1);83 if success == 0 {84 let result = String::from_utf8(buf_intern).expect("error should be valid string");85 bail!(ImportCallbackError(result));86 }8788 let found_here_raw = unsafe { CStr::from_ptr(found_here) };89 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(90 found_here_raw.to_str().unwrap(),91 )));92 unsafe {93 let _ = CString::from_raw(found_here);94 }9596 let mut out = self.out.borrow_mut();97 if !out.contains_key(&found_here_buf) {98 out.insert(found_here_buf.clone(), buf_intern);99 }100101 Ok(found_here_buf)102 }103 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {104 Ok(self.out.borrow().get(resolved).unwrap().clone())105 }106}107108109110111#[no_mangle]112pub unsafe extern "C" fn jsonnet_import_callback(113 vm: &VM,114 cb: JsonnetImportCallback,115 ctx: *mut c_void,116) {117 vm.replace_import_resolver(CallbackImportResolver {118 cb,119 ctx,120 out: RefCell::new(HashMap::new()),121 });122}123124125126127#[no_mangle]128pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {129 let cstr = unsafe { CStr::from_ptr(path) };130 let path = PathBuf::from(cstr.to_str().unwrap());131 vm.add_jpath(path);132}