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

difftreelog

source

bindings/jsonnet/src/lib.rs9.1 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.  Conforms to semantic versioning29/// http://semver.org/ If this does not match LIB_JSONNET_VERSION then there is a mismatch between30/// header and compiled library.31#[no_mangle]32pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {33	b"v0.16.0\0"34}3536unsafe fn parse_path(input: &CStr) -> Cow<Path> {37	#[cfg(target_family = "unix")]38	{39		use std::os::unix::ffi::OsStrExt;40		let str = OsStr::from_bytes(input.to_bytes());41		Cow::Borrowed(Path::new(str))42	}43	#[cfg(target_family = "windows")]44	{45		use std::os::windows::ffi::OsStringExt;46		let str = input.to_str().expect("input is not utf8");47		let wide = str.encode_utf16().collect::<Vec<_>>();48		let wide = OsString::from_wide(&wide);49		Cow::Owned(PathBuf::new(wide))50	}51	#[cfg(not(any(target_family = "unix", target_family = "windows")))]52	{53		compile_error!("unsupported os")54	}55}5657unsafe fn unparse_path(input: &Path) -> Cow<CStr> {58	#[cfg(target_family = "unix")]59	{60		use std::os::unix::ffi::OsStrExt;61		let str = CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it");62		Cow::Owned(str)63	}64	#[cfg(not(any(target_family = "unix", target_family = "windows")))]65	{66		compile_error!("unsupported os")67	}68}6970/// Create a new Jsonnet virtual machine.71#[no_mangle]72pub extern "C" fn jsonnet_make() -> *mut State {73	let state = State::default();74	state.settings_mut().import_resolver = Box::new(FileImportResolver::default());75	state.settings_mut().context_initializer = Box::new(jrsonnet_stdlib::ContextInitializer::new(76		state.clone(),77		PathResolver::new_cwd_fallback(),78	));79	Box::into_raw(Box::new(state))80}8182/// Complement of `jsonnet_vm_make`83#[no_mangle]84#[allow(clippy::boxed_local)]85pub extern "C" fn jsonnet_destroy(vm: Box<State>) {86	drop(vm);87}8889/// Set the maximum stack depth.90#[no_mangle]91pub extern "C" fn jsonnet_max_stack(vm: &State, v: c_uint) {92	vm.settings_mut().max_stack = v as usize;93}9495/// Set the number of objects required before a garbage collection cycle is allowed.96///97/// No-op for now98#[no_mangle]99pub extern "C" fn jsonnet_gc_min_objects(_vm: &State, _v: c_uint) {}100101/// Run the garbage collector after this amount of growth in the number of objects102///103/// No-op for now104#[no_mangle]105pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &State, _v: c_double) {}106107/// Expect a string as output and don't JSON encode it.108#[no_mangle]109pub extern "C" fn jsonnet_string_output(vm: &State, v: c_int) {110	match v {111		1 => vm.set_manifest_format(ManifestFormat::String),112		0 => vm.set_manifest_format(ManifestFormat::Json {113			padding: 4,114			#[cfg(feature = "exp-preserve-order")]115			preserve_order: false,116		}),117		_ => panic!("incorrect output format"),118	}119}120121/// Allocate, resize, or free a buffer.  This will abort if the memory cannot be allocated.  It will122/// only return NULL if sz was zero.123///124/// # Safety125///126/// `buf` should be either previosly allocated by this library, or NULL127///128/// This function is most definitely broken, but it works somehow, see TODO inside129#[no_mangle]130pub unsafe extern "C" fn jsonnet_realloc(_vm: &State, buf: *mut u8, sz: usize) -> *mut u8 {131	if buf.is_null() {132		if sz == 0 {133			return std::ptr::null_mut();134		}135		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());136	}137	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D138	// OR (Alternative way of fixing this TODO)139	// TODO: Standard allocator uses malloc, and it doesn't uses allocation size,140	// TODO: so it should work in normal cases. Maybe force allocator for this library?141	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();142	if sz == 0 {143		std::alloc::dealloc(buf, old_layout);144		return std::ptr::null_mut();145	}146	std::alloc::realloc(buf, old_layout, sz)147}148149/// Clean up a JSON subtree.150///151/// This is useful if you want to abort with an error mid-way through building a complex value.152#[no_mangle]153#[allow(clippy::boxed_local)]154pub extern "C" fn jsonnet_json_destroy(_vm: &State, v: Box<Val>) {155	drop(v);156}157158/// Set the number of lines of stack trace to display (0 for all of them).159#[no_mangle]160pub extern "C" fn jsonnet_max_trace(vm: &State, v: c_uint) {161	vm.set_max_trace(v as usize)162}163164/// Evaluate a file containing Jsonnet code, return a JSON string.165///166/// The returned string should be cleaned up with jsonnet_realloc.167///168/// # Safety169///170/// `filename` should be a \0-terminated string171#[no_mangle]172pub unsafe extern "C" fn jsonnet_evaluate_file(173	vm: &State,174	filename: *const c_char,175	error: &mut c_int,176) -> *const c_char {177	let filename = parse_path(CStr::from_ptr(filename));178	match vm179		.import(&filename)180		.and_then(|v| vm.with_tla(v))181		.and_then(|v| vm.manifest(v))182	{183		Ok(v) => {184			*error = 0;185			CString::new(&*v as &str).unwrap().into_raw()186		}187		Err(e) => {188			*error = 1;189			let out = vm.stringify_err(&e);190			CString::new(&out as &str).unwrap().into_raw()191		}192	}193}194195/// Evaluate a string containing Jsonnet code, return a JSON string.196///197/// The returned string should be cleaned up with jsonnet_realloc.198///199/// # Safety200///201/// `filename`, `snippet` should be a \0-terminated strings202#[no_mangle]203pub unsafe extern "C" fn jsonnet_evaluate_snippet(204	vm: &State,205	filename: *const c_char,206	snippet: *const c_char,207	error: &mut c_int,208) -> *const c_char {209	let filename = CStr::from_ptr(filename);210	let snippet = CStr::from_ptr(snippet);211	match vm212		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())213		.and_then(|v| vm.with_tla(v))214		.and_then(|v| vm.manifest(v))215	{216		Ok(v) => {217			*error = 0;218			CString::new(&*v as &str).unwrap().into_raw()219		}220		Err(e) => {221			*error = 1;222			let out = vm.stringify_err(&e);223			CString::new(&out as &str).unwrap().into_raw()224		}225	}226}227228fn multi_to_raw(multi: Vec<(IStr, IStr)>) -> *const c_char {229	let mut out = Vec::new();230	for (i, (k, v)) in multi.iter().enumerate() {231		if i != 0 {232			out.push(0);233		}234		out.extend_from_slice(k.as_bytes());235		out.push(0);236		out.extend_from_slice(v.as_bytes());237	}238	out.push(0);239	out.push(0);240	let v = out.as_ptr();241	std::mem::forget(out);242	v as *const c_char243}244245/// # Safety246#[no_mangle]247pub unsafe extern "C" fn jsonnet_evaluate_file_multi(248	vm: &State,249	filename: *const c_char,250	error: &mut c_int,251) -> *const c_char {252	let filename = parse_path(CStr::from_ptr(filename));253	match vm254		.import(&filename)255		.and_then(|v| vm.with_tla(v))256		.and_then(|v| vm.manifest_multi(v))257	{258		Ok(v) => {259			*error = 0;260			multi_to_raw(v)261		}262		Err(e) => {263			*error = 1;264			let out = vm.stringify_err(&e);265			CString::new(&out as &str).unwrap().into_raw()266		}267	}268}269270/// # Safety271#[no_mangle]272pub unsafe extern "C" fn jsonnet_evaluate_snippet_multi(273	vm: &State,274	filename: *const c_char,275	snippet: *const c_char,276	error: &mut c_int,277) -> *const c_char {278	let filename = CStr::from_ptr(filename);279	let snippet = CStr::from_ptr(snippet);280	match vm281		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())282		.and_then(|v| vm.with_tla(v))283		.and_then(|v| vm.manifest_multi(v))284	{285		Ok(v) => {286			*error = 0;287			multi_to_raw(v)288		}289		Err(e) => {290			*error = 1;291			let out = vm.stringify_err(&e);292			CString::new(&out as &str).unwrap().into_raw()293		}294	}295}296297fn stream_to_raw(multi: Vec<IStr>) -> *const c_char {298	let mut out = Vec::new();299	for (i, v) in multi.iter().enumerate() {300		if i != 0 {301			out.push(0);302		}303		out.extend_from_slice(v.as_bytes());304	}305	out.push(0);306	out.push(0);307	let v = out.as_ptr();308	std::mem::forget(out);309	v as *const c_char310}311312/// # Safety313#[no_mangle]314pub unsafe extern "C" fn jsonnet_evaluate_file_stream(315	vm: &State,316	filename: *const c_char,317	error: &mut c_int,318) -> *const c_char {319	let filename = parse_path(CStr::from_ptr(filename));320	match vm321		.import(&filename)322		.and_then(|v| vm.with_tla(v))323		.and_then(|v| vm.manifest_stream(v))324	{325		Ok(v) => {326			*error = 0;327			stream_to_raw(v)328		}329		Err(e) => {330			*error = 1;331			let out = vm.stringify_err(&e);332			CString::new(&out as &str)333				.expect("there should be no \\0 in the error string")334				.into_raw()335		}336	}337}338339/// # Safety340#[no_mangle]341pub unsafe extern "C" fn jsonnet_evaluate_snippet_stream(342	vm: &State,343	filename: *const c_char,344	snippet: *const c_char,345	error: &mut c_int,346) -> *const c_char {347	let filename = CStr::from_ptr(filename);348	let snippet = CStr::from_ptr(snippet);349	match vm350		.evaluate_snippet(351			filename.to_str().expect("filename is not utf-8"),352			snippet.to_str().expect("snippet is not utf-8"),353		)354		.and_then(|v| vm.with_tla(v))355		.and_then(|v| vm.manifest_stream(v))356	{357		Ok(v) => {358			*error = 0;359			stream_to_raw(v)360		}361		Err(e) => {362			*error = 1;363			let out = vm.stringify_err(&e);364			CString::new(&out as &str)365				.expect("there should be no \\0 in the error string")366				.into_raw()367		}368	}369}