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

difftreelog

source

bindings/jsonnet/src/lib.rs9.0 KiBsourcehistory
1#[cfg(feature = "interop")]2pub mod interop;34pub mod import;5pub mod native;6pub mod val_extract;7pub mod val_make;8pub mod val_modify;9pub mod vars_tlas;1011use std::{12	alloc::Layout,13	borrow::Cow,14	ffi::{CStr, CString, OsStr},15	os::raw::{c_char, c_double, c_int, c_uint},16	path::Path,17};1819use jrsonnet_evaluator::{20	trace::PathResolver, FileImportResolver, IStr, ManifestFormat, State, Val,21};2223/// WASM stub24#[cfg(target_arch = "wasm32")]25#[no_mangle]26pub extern "C" fn _start() {}2728/// Return the version string of the Jsonnet interpreter.29/// Conforms to [semantic versioning](http://semver.org/).30/// If this does not match `LIB_JSONNET_VERSION`31/// then there is a mismatch between header and compiled library.32#[no_mangle]33pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {34	b"v0.16.0\0"35}3637unsafe fn parse_path(input: &CStr) -> Cow<Path> {38	#[cfg(target_family = "unix")]39	{40		use std::os::unix::ffi::OsStrExt;41		let str = OsStr::from_bytes(input.to_bytes());42		Cow::Borrowed(Path::new(str))43	}44	#[cfg(not(target_family = "unix"))]45	{46		let string = input.to_str().expect("bad utf-8");47		Cow::Borrowed(string.as_ref())48	}49}5051unsafe fn unparse_path(input: &Path) -> Cow<CStr> {52	#[cfg(target_family = "unix")]53	{54		use std::os::unix::ffi::OsStrExt;55		let str = CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it");56		Cow::Owned(str)57	}58	#[cfg(not(target_family = "unix"))]59	{60		let str = input.as_os_str().to_str().expect("bad utf-8");61		let cstr = CString::new(str).expect("input has NUL inside");62		Cow::Owned(cstr)63	}64}6566/// Creates a new Jsonnet virtual machine.67#[no_mangle]68#[allow(clippy::box_default)]69pub extern "C" fn jsonnet_make() -> *mut State {70	let state = State::default();71	state.settings_mut().import_resolver = Box::new(FileImportResolver::default());72	state.settings_mut().context_initializer = Box::new(jrsonnet_stdlib::ContextInitializer::new(73		state.clone(),74		PathResolver::new_cwd_fallback(),75	));76	Box::into_raw(Box::new(state))77}7879/// Complement of [`jsonnet_vm_make`].80#[no_mangle]81#[allow(clippy::boxed_local)]82pub extern "C" fn jsonnet_destroy(vm: Box<State>) {83	drop(vm);84}8586/// Set the maximum stack depth.87#[no_mangle]88pub extern "C" fn jsonnet_max_stack(vm: &State, v: c_uint) {89	vm.settings_mut().max_stack = v as usize;90}9192/// Set the number of objects required before a garbage collection cycle is allowed.93///94/// No-op for now95#[no_mangle]96pub extern "C" fn jsonnet_gc_min_objects(_vm: &State, _v: c_uint) {}9798/// Run the garbage collector after this amount of growth in the number of objects99///100/// No-op for now101#[no_mangle]102pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &State, _v: c_double) {}103104/// Expect a string as output and don't JSON encode it.105#[no_mangle]106pub extern "C" fn jsonnet_string_output(vm: &State, v: c_int) {107	match v {108		1 => vm.set_manifest_format(ManifestFormat::String),109		0 => vm.set_manifest_format(ManifestFormat::Json {110			padding: 4,111			#[cfg(feature = "exp-preserve-order")]112			preserve_order: false,113		}),114		_ => panic!("incorrect output format"),115	}116}117118/// Allocate, resize, or free a buffer.  This will abort if the memory cannot be allocated. It will119/// only return NULL if sz was zero.120///121/// # Safety122///123/// `buf` should be either previosly allocated by this library, or NULL124///125/// This function is most definitely broken, but it works somehow, see TODO inside126#[no_mangle]127pub unsafe extern "C" fn jsonnet_realloc(_vm: &State, buf: *mut u8, sz: usize) -> *mut u8 {128	if buf.is_null() {129		if sz == 0 {130			return std::ptr::null_mut();131		}132		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());133	}134	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D135	// OR (Alternative way of fixing this TODO)136	// TODO: Standard allocator uses malloc, and it doesn't uses allocation size,137	// TODO: so it should work in normal cases. Maybe force allocator for this library?138	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();139	if sz == 0 {140		std::alloc::dealloc(buf, old_layout);141		return std::ptr::null_mut();142	}143	std::alloc::realloc(buf, old_layout, sz)144}145146/// Clean up a JSON subtree.147///148/// This is useful if you want to abort with an error mid-way through building a complex value.149#[no_mangle]150#[allow(clippy::boxed_local)]151pub extern "C" fn jsonnet_json_destroy(_vm: &State, v: Box<Val>) {152	drop(v);153}154155/// Set the number of lines of stack trace to display (0 for all of them).156#[no_mangle]157pub extern "C" fn jsonnet_max_trace(vm: &State, v: c_uint) {158	vm.set_max_trace(v as usize)159}160161/// Evaluate a file containing Jsonnet code, return a JSON string.162///163/// The returned string should be cleaned up with jsonnet_realloc.164///165/// # Safety166///167/// `filename` should be a NUL-terminated string168#[no_mangle]169pub unsafe extern "C" fn jsonnet_evaluate_file(170	vm: &State,171	filename: *const c_char,172	error: &mut c_int,173) -> *const c_char {174	let filename = parse_path(CStr::from_ptr(filename));175	match vm176		.import(&filename)177		.and_then(|v| vm.with_tla(v))178		.and_then(|v| vm.manifest(v))179	{180		Ok(v) => {181			*error = 0;182			CString::new(&*v as &str).unwrap().into_raw()183		}184		Err(e) => {185			*error = 1;186			let out = vm.stringify_err(&e);187			CString::new(&out as &str).unwrap().into_raw()188		}189	}190}191192/// Evaluate a string containing Jsonnet code, return a JSON string.193///194/// The returned string should be cleaned up with jsonnet_realloc.195///196/// # Safety197///198/// `filename`, `snippet` should be a NUL-terminated strings199#[no_mangle]200pub unsafe extern "C" fn jsonnet_evaluate_snippet(201	vm: &State,202	filename: *const c_char,203	snippet: *const c_char,204	error: &mut c_int,205) -> *const c_char {206	let filename = CStr::from_ptr(filename);207	let snippet = CStr::from_ptr(snippet);208	match vm209		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())210		.and_then(|v| vm.with_tla(v))211		.and_then(|v| vm.manifest(v))212	{213		Ok(v) => {214			*error = 0;215			CString::new(&*v as &str).unwrap().into_raw()216		}217		Err(e) => {218			*error = 1;219			let out = vm.stringify_err(&e);220			CString::new(&out as &str).unwrap().into_raw()221		}222	}223}224225fn multi_to_raw(multi: Vec<(IStr, IStr)>) -> *const c_char {226	let mut out = Vec::new();227	for (i, (k, v)) in multi.iter().enumerate() {228		if i != 0 {229			out.push(0);230		}231		out.extend_from_slice(k.as_bytes());232		out.push(0);233		out.extend_from_slice(v.as_bytes());234	}235	out.push(0);236	out.push(0);237	let v = out.as_ptr();238	std::mem::forget(out);239	v as *const c_char240}241242/// # Safety243#[no_mangle]244pub unsafe extern "C" fn jsonnet_evaluate_file_multi(245	vm: &State,246	filename: *const c_char,247	error: &mut c_int,248) -> *const c_char {249	let filename = parse_path(CStr::from_ptr(filename));250	match vm251		.import(&filename)252		.and_then(|v| vm.with_tla(v))253		.and_then(|v| vm.manifest_multi(v))254	{255		Ok(v) => {256			*error = 0;257			multi_to_raw(v)258		}259		Err(e) => {260			*error = 1;261			let out = vm.stringify_err(&e);262			CString::new(&out as &str).unwrap().into_raw()263		}264	}265}266267/// # Safety268#[no_mangle]269pub unsafe extern "C" fn jsonnet_evaluate_snippet_multi(270	vm: &State,271	filename: *const c_char,272	snippet: *const c_char,273	error: &mut c_int,274) -> *const c_char {275	let filename = CStr::from_ptr(filename);276	let snippet = CStr::from_ptr(snippet);277	match vm278		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())279		.and_then(|v| vm.with_tla(v))280		.and_then(|v| vm.manifest_multi(v))281	{282		Ok(v) => {283			*error = 0;284			multi_to_raw(v)285		}286		Err(e) => {287			*error = 1;288			let out = vm.stringify_err(&e);289			CString::new(&out as &str).unwrap().into_raw()290		}291	}292}293294fn stream_to_raw(multi: Vec<IStr>) -> *const c_char {295	let mut out = Vec::new();296	for (i, v) in multi.iter().enumerate() {297		if i != 0 {298			out.push(0);299		}300		out.extend_from_slice(v.as_bytes());301	}302	out.push(0);303	out.push(0);304	let v = out.as_ptr();305	std::mem::forget(out);306	v as *const c_char307}308309/// # Safety310#[no_mangle]311pub unsafe extern "C" fn jsonnet_evaluate_file_stream(312	vm: &State,313	filename: *const c_char,314	error: &mut c_int,315) -> *const c_char {316	let filename = parse_path(CStr::from_ptr(filename));317	match vm318		.import(&filename)319		.and_then(|v| vm.with_tla(v))320		.and_then(|v| vm.manifest_stream(v))321	{322		Ok(v) => {323			*error = 0;324			stream_to_raw(v)325		}326		Err(e) => {327			*error = 1;328			let out = vm.stringify_err(&e);329			CString::new(&out as &str)330				.expect("there should be no \\0 in the error string")331				.into_raw()332		}333	}334}335336/// # Safety337#[no_mangle]338pub unsafe extern "C" fn jsonnet_evaluate_snippet_stream(339	vm: &State,340	filename: *const c_char,341	snippet: *const c_char,342	error: &mut c_int,343) -> *const c_char {344	let filename = CStr::from_ptr(filename);345	let snippet = CStr::from_ptr(snippet);346	match vm347		.evaluate_snippet(348			filename.to_str().expect("filename is not utf-8"),349			snippet.to_str().expect("snippet is not utf-8"),350		)351		.and_then(|v| vm.with_tla(v))352		.and_then(|v| vm.manifest_stream(v))353	{354		Ok(v) => {355			*error = 0;356			stream_to_raw(v)357		}358		Err(e) => {359			*error = 1;360			let out = vm.stringify_err(&e);361			CString::new(&out as &str)362				.expect("there should be no \\0 in the error string")363				.into_raw()364		}365	}366}