git.delta.rocks / jrsonnet / refs/commits / 2b4e36fe5944

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,17};18use jrsonnet_gcmodule::Trace;19use jrsonnet_parser::{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	success: &mut c_int,29) -> *mut c_char;3031/// Resolves imports using callback32#[derive(Trace)]33pub struct CallbackImportResolver {34	#[trace(skip)]35	cb: JsonnetImportCallback,36	#[trace(skip)]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();56		let mut success: i32 = 0;57		let result_ptr = unsafe {58			(self.cb)(59				self.ctx,60				base.as_ptr(),61				rel.as_ptr(),62				&mut (found_here as *const _),63				&mut success,64			)65		};66		let result_raw = unsafe { CStr::from_ptr(result_ptr) };67		let result_str = result_raw.to_str().unwrap();68		assert!(success == 0 || success == 1);69		if success == 0 {70			unsafe { CString::from_raw(result_ptr) };71			let result = result_str.to_owned();72			throw!(ImportCallbackError(result));73		}7475		let found_here_raw = unsafe { CStr::from_ptr(found_here) };76		let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(77			found_here_raw.to_str().unwrap(),78		)));79		unsafe {80			let _ = CString::from_raw(found_here);81		}8283		let mut out = self.out.borrow_mut();84		if !out.contains_key(&found_here_buf) {85			out.insert(found_here_buf.clone(), result_str.into());86			unsafe { CString::from_raw(result_ptr) };87		}8889		Ok(found_here_buf)90	}91	fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {92		Ok(self.out.borrow().get(resolved).unwrap().clone())93	}9495	fn as_any(&self) -> &dyn Any {96		self97	}98}99100/// # Safety101///102/// It should be safe to call `cb` using valid values with passed `ctx`103#[no_mangle]104pub unsafe extern "C" fn jsonnet_import_callback(105	vm: &VM,106	cb: JsonnetImportCallback,107	ctx: *mut c_void,108) {109	vm.state110		.set_import_resolver(Box::new(CallbackImportResolver {111			cb,112			ctx,113			out: RefCell::new(HashMap::new()),114		}))115}116117/// # Safety118///119/// `path` should be a NUL-terminated string120#[no_mangle]121pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {122	let cstr = CStr::from_ptr(path);123	let path = PathBuf::from(cstr.to_str().unwrap());124	let any_resolver = vm.state.import_resolver();125	let resolver = any_resolver126		.as_any()127		.downcast_ref::<FileImportResolver>()128		.expect("jpaths are not compatible with callback imports!");129	resolver.add_jpath(path);130}