git.delta.rocks / jrsonnet / refs/commits / d14f5fd9b388

difftreelog

source

bindings/jsonnet/src/import.rs3.3 KiBsourcehistory
1//! Import resolution manipulation utilities23use 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;3233/// Resolves imports using callback34#[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}107108/// # Safety109///110/// It should be safe to call `cb` using valid values with passed `ctx`111#[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}123124/// # Safety125///126/// `path` should be a NUL-terminated string127#[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}