123use std::{4 alloc::Layout,5 cell::RefCell,6 collections::HashMap,7 env::current_dir,8 ffi::{CStr, CString, c_void},9 os::raw::{c_char, c_int},10 path::PathBuf,11 ptr::null_mut,12};1314use jrsonnet_evaluator::{15 AsPathLike, ImportResolver, ResolvePath, bail,16 error::{ErrorKind::*, Result},17};18use jrsonnet_gcmodule::Acyclic;19use jrsonnet_ir::{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 buf: *mut *mut c_char,29 buflen: *mut usize,30) -> c_int;313233#[derive(Acyclic)]34pub struct CallbackImportResolver {35 cb: JsonnetImportCallback,36 ctx: *mut c_void,37 out: RefCell<HashMap<SourcePath, Vec<u8>>>,38}39impl ImportResolver for CallbackImportResolver {40 fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {41 let base = if let Some(p) = from.downcast_ref::<SourceFile>() {42 let mut o = p.path().to_owned();43 o.pop();44 o45 } else if let Some(d) = from.downcast_ref::<SourceDirectory>() {46 d.path().to_owned()47 } else if from.is_default() {48 current_dir().map_err(|e| ImportIo(e.to_string()))?49 } else {50 unreachable!("can't resolve this path");51 };52 let base = unsafe { crate::unparse_path(&base) };53 let rel = path.as_path();54 let rel = match rel {55 ResolvePath::Str(s) => CString::new(s.as_bytes()).unwrap(),56 ResolvePath::Path(p) => unsafe { crate::unparse_path(p) },57 };58 let found_here: *mut c_char = null_mut();5960 let mut buf = null_mut();61 let mut buf_len = 0;62 let success = unsafe {63 (self.cb)(64 self.ctx,65 base.as_ptr(),66 rel.as_ptr(),67 &mut found_here.cast_const(),68 &raw mut buf,69 &raw mut buf_len,70 )71 };72 let buf_slice: &[u8] = unsafe { std::slice::from_raw_parts(buf.cast(), buf_len) };73 unsafe {74 std::alloc::dealloc(75 buf.cast(),76 Layout::from_size_align(buf_len, 1).expect("layout is valid"),77 );78 };79 let buf_intern = buf_slice.to_vec();8081 assert!(success == 0 || success == 1);82 if success == 0 {83 let result = String::from_utf8(buf_intern).expect("error should be valid string");84 bail!(ImportCallbackError(result));85 }8687 let found_here_raw = unsafe { CStr::from_ptr(found_here) };88 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(89 found_here_raw.to_str().unwrap(),90 )));91 unsafe {92 let _ = CString::from_raw(found_here);93 }9495 let mut out = self.out.borrow_mut();96 if !out.contains_key(&found_here_buf) {97 out.insert(found_here_buf.clone(), buf_intern);98 }99100 Ok(found_here_buf)101 }102 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {103 Ok(self.out.borrow().get(resolved).unwrap().clone())104 }105}106107108109110#[no_mangle]111pub unsafe extern "C" fn jsonnet_import_callback(112 vm: &VM,113 cb: JsonnetImportCallback,114 ctx: *mut c_void,115) {116 vm.replace_import_resolver(CallbackImportResolver {117 cb,118 ctx,119 out: RefCell::new(HashMap::new()),120 });121}122123124125126#[no_mangle]127pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {128 let cstr = unsafe { CStr::from_ptr(path) };129 let path = PathBuf::from(cstr.to_str().unwrap());130 vm.add_jpath(path);131}