git.delta.rocks / jrsonnet / refs/commits / 57f709a58e12

difftreelog

source

bindings/jsonnet/src/import.rs3.4 KiBsourcehistory
1//! Import resolution manipulation utilities23use 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	bail,17	error::{ErrorKind::*, Result},18	FileImportResolver, ImportResolver,19};20use jrsonnet_gcmodule::Trace;21use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};2223use crate::VM;2425pub type JsonnetImportCallback = unsafe extern "C" fn(26	ctx: *mut c_void,27	base: *const c_char,28	rel: *const c_char,29	found_here: *mut *const c_char,30	buf: *mut *mut c_char,31	buflen: *mut usize,32) -> c_int;3334/// Resolves imports using callback35#[derive(Trace)]36pub struct CallbackImportResolver {37	#[trace(skip)]38	cb: JsonnetImportCallback,39	#[trace(skip)]40	ctx: *mut c_void,41	out: RefCell<HashMap<SourcePath, Vec<u8>>>,42}43impl ImportResolver for CallbackImportResolver {44	fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {45		let base = if let Some(p) = from.downcast_ref::<SourceFile>() {46			let mut o = p.path().to_owned();47			o.pop();48			o49		} else if let Some(d) = from.downcast_ref::<SourceDirectory>() {50			d.path().to_owned()51		} else if from.is_default() {52			current_dir().map_err(|e| ImportIo(e.to_string()))?53		} else {54			unreachable!("can't resolve this path");55		};56		let base = unsafe { crate::unparse_path(&base) };57		let rel = CString::new(path).unwrap();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 as *const _),68				&mut buf,69				&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	}105106	fn as_any(&self) -> &dyn Any {107		self108	}109}110111/// # Safety112///113/// It should be safe to call `cb` using valid values with passed `ctx`114#[no_mangle]115pub unsafe extern "C" fn jsonnet_import_callback(116	vm: &VM,117	cb: JsonnetImportCallback,118	ctx: *mut c_void,119) {120	vm.state.set_import_resolver(CallbackImportResolver {121		cb,122		ctx,123		out: RefCell::new(HashMap::new()),124	})125}126127/// # Safety128///129/// `path` should be a NUL-terminated string130#[no_mangle]131pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {132	let cstr = unsafe { 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}