git.delta.rocks / jrsonnet / refs/heads / master

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::{CStr, CString, c_void},9	os::raw::{c_char, c_int},10	path::PathBuf,11	ptr::null_mut,12};1314use jrsonnet_evaluator::{15	AsPathLike, ImportResolver, ResolvePath, bail,16	error::{ErrorKind::*, Result},17};18use jrsonnet_gcmodule::Acyclic;19use jrsonnet_ir::{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	buf: *mut *mut c_char,29	buflen: *mut usize,30) -> c_int;3132/// Resolves imports using callback33#[derive(Acyclic)]34pub struct CallbackImportResolver {35	cb: JsonnetImportCallback,36	ctx: *mut c_void,37	out: RefCell<HashMap<SourcePath, Vec<u8>>>,38}39impl ImportResolver for CallbackImportResolver {40	fn resolve_from(&self, from: &SourcePath, path: &dyn AsPathLike) -> Result<SourcePath> {41		let base = if let Some(p) = from.downcast_ref::<SourceFile>() {42			let mut o = p.path().to_owned();43			o.pop();44			o45		} else if let Some(d) = from.downcast_ref::<SourceDirectory>() {46			d.path().to_owned()47		} else if from.is_default() {48			current_dir().map_err(|e| ImportIo(e.to_string()))?49		} else {50			unreachable!("can't resolve this path");51		};52		let base = unsafe { crate::unparse_path(&base) };53		let rel = path.as_path();54		let rel = match rel {55			ResolvePath::Str(s) => CString::new(s.as_bytes()).unwrap(),56			ResolvePath::Path(p) => unsafe { crate::unparse_path(p) },57		};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.cast_const(),68				&raw mut buf,69				&raw 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	}105}106107/// # Safety108///109/// It should be safe to call `cb` using valid values with passed `ctx`110#[unsafe(no_mangle)]111pub unsafe extern "C" fn jsonnet_import_callback(112	vm: &VM,113	cb: JsonnetImportCallback,114	ctx: *mut c_void,115) {116	vm.replace_import_resolver(CallbackImportResolver {117		cb,118		ctx,119		out: RefCell::new(HashMap::new()),120	});121}122123/// # Safety124///125/// `path` should be a NUL-terminated string126#[unsafe(no_mangle)]127pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {128	let cstr = unsafe { CStr::from_ptr(path) };129	let path = PathBuf::from(cstr.to_str().unwrap());130	vm.add_jpath(path);131}