git.delta.rocks / jrsonnet / refs/commits / 85ada850c7eb

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	error::{ErrorKind::*, Result},17	throw, FileImportResolver, ImportResolver,18};19use jrsonnet_gcmodule::Trace;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(Trace)]35pub struct CallbackImportResolver {36	#[trace(skip)]37	cb: JsonnetImportCallback,38	#[trace(skip)]39	ctx: *mut c_void,40	out: RefCell<HashMap<SourcePath, Vec<u8>>>,41}42impl ImportResolver for CallbackImportResolver {43	fn resolve_from(&self, from: &SourcePath, path: &str) -> Result<SourcePath> {44		let base = if let Some(p) = from.downcast_ref::<SourceFile>() {45			let mut o = p.path().to_owned();46			o.pop();47			o48		} else if let Some(d) = from.downcast_ref::<SourceDirectory>() {49			d.path().to_owned()50		} else if from.is_default() {51			current_dir().map_err(|e| ImportIo(e.to_string()))?52		} else {53			unreachable!("can't resolve this path");54		};55		let base = unsafe { crate::unparse_path(&base) };56		let rel = CString::new(path).unwrap();57		let found_here: *mut c_char = null_mut();5859		let mut buf = null_mut();60		let mut buf_len = 0;61		let success = unsafe {62			(self.cb)(63				self.ctx,64				base.as_ptr(),65				rel.as_ptr(),66				&mut (found_here as *const _),67				&mut buf,68				&mut buf_len,69			)70		};71		let buf_slice: &[u8] = unsafe { std::slice::from_raw_parts(buf.cast(), buf_len) };72		unsafe {73			std::alloc::dealloc(74				buf.cast(),75				Layout::from_size_align(buf_len, 1).expect("layout is valid"),76			);77		};78		let buf_intern = buf_slice.to_vec();7980		assert!(success == 0 || success == 1);81		if success == 0 {82			let result = String::from_utf8(buf_intern).expect("error should be valid string");83			throw!(ImportCallbackError(result));84		}8586		let found_here_raw = unsafe { CStr::from_ptr(found_here) };87		let found_here_buf = SourcePath::new(SourceFile::new(PathBuf::from(88			found_here_raw.to_str().unwrap(),89		)));90		unsafe {91			let _ = CString::from_raw(found_here);92		}9394		let mut out = self.out.borrow_mut();95		if !out.contains_key(&found_here_buf) {96			out.insert(found_here_buf.clone(), buf_intern);97		}9899		Ok(found_here_buf)100	}101	fn load_file_contents(&self, resolved: &SourcePath) -> Result<Vec<u8>> {102		Ok(self.out.borrow().get(resolved).unwrap().clone())103	}104105	fn as_any(&self) -> &dyn Any {106		self107	}108}109110/// # Safety111///112/// It should be safe to call `cb` using valid values with passed `ctx`113#[no_mangle]114pub unsafe extern "C" fn jsonnet_import_callback(115	vm: &VM,116	cb: JsonnetImportCallback,117	ctx: *mut c_void,118) {119	vm.state.set_import_resolver(CallbackImportResolver {120		cb,121		ctx,122		out: RefCell::new(HashMap::new()),123	})124}125126/// # Safety127///128/// `path` should be a NUL-terminated string129#[no_mangle]130pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {131	let cstr = CStr::from_ptr(path);132	let path = PathBuf::from(cstr.to_str().unwrap());133	let any_resolver = vm.state.import_resolver();134	let resolver = any_resolver135		.as_any()136		.downcast_ref::<FileImportResolver>()137		.expect("jpaths are not compatible with callback imports!");138	resolver.add_jpath(path);139}