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

difftreelog

source

bindings/jsonnet/src/import.rs3.3 KiBsourcehistory
1//! Import resolution manipulation utilities23use std::{4	any::Any,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	error::{Error::*, Result},16	throw, FileImportResolver, ImportResolver, State,17};18use jrsonnet_gcmodule::Trace;19use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath};2021pub type JsonnetImportCallback = unsafe extern "C" fn(22	ctx: *mut c_void,23	base: *const c_char,24	rel: *const c_char,25	found_here: *mut *const c_char,26	success: &mut c_int,27) -> *mut c_char;2829/// Resolves imports using callback30#[derive(Trace)]31pub struct CallbackImportResolver {32	#[trace(skip)]33	cb: JsonnetImportCallback,34	#[trace(skip)]35	ctx: *mut c_void,36	out: RefCell<HashMap<SourcePath, Vec<u8>>>,37}38impl ImportResolver for CallbackImportResolver {39	fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {40		let base = if let Some(p) = from.downcast_ref::<SourceFile>() {41			let mut o = p.path().to_owned();42			o.pop();43			o44		} else if let Some(d) = from.downcast_ref::<SourceDirectory>() {45			d.path().to_owned()46		} else if from.is_default() {47			current_dir().map_err(|e| ImportIo(e.to_string()))?48		} else {49			unreachable!("can't resolve this path");50		};51		let base = unsafe { crate::unparse_path(&base) };52		let rel = CString::new(path).unwrap();53		let found_here: *mut c_char = null_mut();54		let mut success: i32 = 0;55		let result_ptr = unsafe {56			(self.cb)(57				self.ctx,58				base.as_ptr(),59				rel.as_ptr(),60				&mut (found_here as *const _),61				&mut success,62			)63		};64		let result_raw = unsafe { CStr::from_ptr(result_ptr) };65		let result_str = result_raw.to_str().unwrap();66		assert!(success == 0 || success == 1);67		if success == 0 {68			unsafe { CString::from_raw(result_ptr) };69			let result = result_str.to_owned();70			throw!(ImportCallbackError(result));71		}7273		let found_here_raw = unsafe { CStr::from_ptr(found_here) };74		let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(75			found_here_raw.to_str().unwrap(),76		)));77		unsafe {78			let _ = CString::from_raw(found_here);79		}8081		let mut out = self.out.borrow_mut();82		if !out.contains_key(&found_here_buf) {83			out.insert(found_here_buf.clone(), result_str.into());84			unsafe { CString::from_raw(result_ptr) };85		}8687		Ok(found_here_buf)88	}89	fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {90		Ok(self.out.borrow().get(resolved).unwrap().clone())91	}9293	fn as_any(&self) -> &dyn Any {94		self95	}96}9798/// # Safety99///100/// It should be safe to call `cb` using valid values with passed `ctx`101#[no_mangle]102pub unsafe extern "C" fn jsonnet_import_callback(103	vm: &State,104	cb: JsonnetImportCallback,105	ctx: *mut c_void,106) {107	vm.set_import_resolver(Box::new(CallbackImportResolver {108		cb,109		ctx,110		out: RefCell::new(HashMap::new()),111	}))112}113114/// # Safety115///116/// `path` should be a NUL-terminated string117#[no_mangle]118pub unsafe extern "C" fn jsonnet_jpath_add(vm: &State, path: *const c_char) {119	let cstr = CStr::from_ptr(path);120	let path = PathBuf::from(cstr.to_str().unwrap());121	let any_resolver = vm.import_resolver();122	let resolver = any_resolver123		.as_any()124		.downcast_ref::<FileImportResolver>()125		.expect("jpaths are not compatible with callback imports!");126	resolver.add_jpath(path);127}