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 ImportResolver,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: &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();5657 let mut buf = null_mut();58 let mut buf_len = 0;59 let success = unsafe {60 (self.cb)(61 self.ctx,62 base.as_ptr(),63 rel.as_ptr(),64 &mut found_here.cast_const(),65 &mut buf,66 &mut buf_len,67 )68 };69 let buf_slice: &[u8] = unsafe { std::slice::from_raw_parts(buf.cast(), buf_len) };70 unsafe {71 std::alloc::dealloc(72 buf.cast(),73 Layout::from_size_align(buf_len, 1).expect("layout is valid"),74 );75 };76 let buf_intern = buf_slice.to_vec();7778 assert!(success == 0 || success == 1);79 if success == 0 {80 let result = String::from_utf8(buf_intern).expect("error should be valid string");81 bail!(ImportCallbackError(result));82 }8384 let found_here_raw = unsafe { CStr::from_ptr(found_here) };85 let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(86 found_here_raw.to_str().unwrap(),87 )));88 unsafe {89 let _ = CString::from_raw(found_here);90 }9192 let mut out = self.out.borrow_mut();93 if !out.contains_key(&found_here_buf) {94 out.insert(found_here_buf.clone(), buf_intern);95 }9697 Ok(found_here_buf)98 }99 fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {100 Ok(self.out.borrow().get(resolved).unwrap().clone())101 }102}103104105106107#[no_mangle]108pub unsafe extern "C" fn jsonnet_import_callback(109 vm: &VM,110 cb: JsonnetImportCallback,111 ctx: *mut c_void,112) {113 vm.replace_import_resolver(CallbackImportResolver {114 cb,115 ctx,116 out: RefCell::new(HashMap::new()),117 });118}119120121122123#[no_mangle]124pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {125 let cstr = unsafe { CStr::from_ptr(path) };126 let path = PathBuf::from(cstr.to_str().unwrap());127 vm.add_jpath(path);128}