git.delta.rocks / jrsonnet / refs/commits / 097494ede5f1

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.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(target_family = "windows")]45	{46		use std::os::windows::ffi::OsStringExt;47		let str = input.to_str().expect("input is not utf8");48		let wide = str.encode_utf16().collect::<Vec<_>>();49		let wide = OsString::from_wide(&wide);50		Cow::Owned(PathBuf::new(wide))51	}52	#[cfg(not(any(target_family = "unix", target_family = "windows")))]53	{54		compile_error!("unsupported os")55	}56}5758unsafe fn unparse_path(input: &Path) -> Cow<CStr> {59	#[cfg(target_family = "unix")]60	{61		use std::os::unix::ffi::OsStrExt;62		let str = CString::new(input.as_os_str().as_bytes()).expect("input has zero byte in it");63		Cow::Owned(str)64	}65	#[cfg(not(any(target_family = "unix", target_family = "windows")))]66	{67		compile_error!("unsupported os")68	}69}7071/// Creates a new Jsonnet virtual machine.72#[no_mangle]73pub extern "C" fn jsonnet_make() -> *mut State {74	let state = State::default();75	state.settings_mut().import_resolver = Box::new(FileImportResolver::default());76	state.settings_mut().context_initializer = Box::new(jrsonnet_stdlib::ContextInitializer::new(77		state.clone(),78		PathResolver::new_cwd_fallback(),79	));80	Box::into_raw(Box::new(state))81}8283/// Complement of [`jsonnet_vm_make`].84#[no_mangle]85#[allow(clippy::boxed_local)]86pub extern "C" fn jsonnet_destroy(vm: Box<State>) {87	drop(vm);88}8990/// Set the maximum stack depth.91#[no_mangle]92pub extern "C" fn jsonnet_max_stack(vm: &State, v: c_uint) {93	vm.settings_mut().max_stack = v as usize;94}9596/// Set the number of objects required before a garbage collection cycle is allowed.97///98/// No-op for now99#[no_mangle]100pub extern "C" fn jsonnet_gc_min_objects(_vm: &State, _v: c_uint) {}101102/// Run the garbage collector after this amount of growth in the number of objects103///104/// No-op for now105#[no_mangle]106pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &State, _v: c_double) {}107108/// Expect a string as output and don't JSON encode it.109#[no_mangle]110pub extern "C" fn jsonnet_string_output(vm: &State, v: c_int) {111	match v {112		1 => vm.set_manifest_format(ManifestFormat::String),113		0 => vm.set_manifest_format(ManifestFormat::Json {114			padding: 4,115			#[cfg(feature = "exp-preserve-order")]116			preserve_order: false,117		}),118		_ => panic!("incorrect output format"),119	}120}121122/// Allocate, resize, or free a buffer.  This will abort if the memory cannot be allocated. It will123/// only return NULL if sz was zero.124///125/// # Safety126///127/// `buf` should be either previosly allocated by this library, or NULL128///129/// This function is most definitely broken, but it works somehow, see TODO inside130#[no_mangle]131pub unsafe extern "C" fn jsonnet_realloc(_vm: &State, buf: *mut u8, sz: usize) -> *mut u8 {132	if buf.is_null() {133		if sz == 0 {134			return std::ptr::null_mut();135		}136		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());137	}138	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D139	// OR (Alternative way of fixing this TODO)140	// TODO: Standard allocator uses malloc, and it doesn't uses allocation size,141	// TODO: so it should work in normal cases. Maybe force allocator for this library?142	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();143	if sz == 0 {144		std::alloc::dealloc(buf, old_layout);145		return std::ptr::null_mut();146	}147	std::alloc::realloc(buf, old_layout, sz)148}149150/// Clean up a JSON subtree.151///152/// This is useful if you want to abort with an error mid-way through building a complex value.153#[no_mangle]154#[allow(clippy::boxed_local)]155pub extern "C" fn jsonnet_json_destroy(_vm: &State, v: Box<Val>) {156	drop(v);157}158159/// Set the number of lines of stack trace to display (0 for all of them).160#[no_mangle]161pub extern "C" fn jsonnet_max_trace(vm: &State, v: c_uint) {162	vm.set_max_trace(v as usize)163}164165/// Evaluate a file containing Jsonnet code, return a JSON string.166///167/// The returned string should be cleaned up with jsonnet_realloc.168///169/// # Safety170///171/// `filename` should be a \0-terminated string172#[no_mangle]173pub unsafe extern "C" fn jsonnet_evaluate_file(174	vm: &State,175	filename: *const c_char,176	error: &mut c_int,177) -> *const c_char {178	let filename = parse_path(CStr::from_ptr(filename));179	match vm180		.import(&filename)181		.and_then(|v| vm.with_tla(v))182		.and_then(|v| vm.manifest(v))183	{184		Ok(v) => {185			*error = 0;186			CString::new(&*v as &str).unwrap().into_raw()187		}188		Err(e) => {189			*error = 1;190			let out = vm.stringify_err(&e);191			CString::new(&out as &str).unwrap().into_raw()192		}193	}194}195196/// Evaluate a string containing Jsonnet code, return a JSON string.197///198/// The returned string should be cleaned up with jsonnet_realloc.199///200/// # Safety201///202/// `filename`, `snippet` should be a \0-terminated strings203#[no_mangle]204pub unsafe extern "C" fn jsonnet_evaluate_snippet(205	vm: &State,206	filename: *const c_char,207	snippet: *const c_char,208	error: &mut c_int,209) -> *const c_char {210	let filename = CStr::from_ptr(filename);211	let snippet = CStr::from_ptr(snippet);212	match vm213		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())214		.and_then(|v| vm.with_tla(v))215		.and_then(|v| vm.manifest(v))216	{217		Ok(v) => {218			*error = 0;219			CString::new(&*v as &str).unwrap().into_raw()220		}221		Err(e) => {222			*error = 1;223			let out = vm.stringify_err(&e);224			CString::new(&out as &str).unwrap().into_raw()225		}226	}227}228229fn multi_to_raw(multi: Vec<(IStr, IStr)>) -> *const c_char {230	let mut out = Vec::new();231	for (i, (k, v)) in multi.iter().enumerate() {232		if i != 0 {233			out.push(0);234		}235		out.extend_from_slice(k.as_bytes());236		out.push(0);237		out.extend_from_slice(v.as_bytes());238	}239	out.push(0);240	out.push(0);241	let v = out.as_ptr();242	std::mem::forget(out);243	v as *const c_char244}245246/// # Safety247#[no_mangle]248pub unsafe extern "C" fn jsonnet_evaluate_file_multi(249	vm: &State,250	filename: *const c_char,251	error: &mut c_int,252) -> *const c_char {253	let filename = parse_path(CStr::from_ptr(filename));254	match vm255		.import(&filename)256		.and_then(|v| vm.with_tla(v))257		.and_then(|v| vm.manifest_multi(v))258	{259		Ok(v) => {260			*error = 0;261			multi_to_raw(v)262		}263		Err(e) => {264			*error = 1;265			let out = vm.stringify_err(&e);266			CString::new(&out as &str).unwrap().into_raw()267		}268	}269}270271/// # Safety272#[no_mangle]273pub unsafe extern "C" fn jsonnet_evaluate_snippet_multi(274	vm: &State,275	filename: *const c_char,276	snippet: *const c_char,277	error: &mut c_int,278) -> *const c_char {279	let filename = CStr::from_ptr(filename);280	let snippet = CStr::from_ptr(snippet);281	match vm282		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())283		.and_then(|v| vm.with_tla(v))284		.and_then(|v| vm.manifest_multi(v))285	{286		Ok(v) => {287			*error = 0;288			multi_to_raw(v)289		}290		Err(e) => {291			*error = 1;292			let out = vm.stringify_err(&e);293			CString::new(&out as &str).unwrap().into_raw()294		}295	}296}297298fn stream_to_raw(multi: Vec<IStr>) -> *const c_char {299	let mut out = Vec::new();300	for (i, v) in multi.iter().enumerate() {301		if i != 0 {302			out.push(0);303		}304		out.extend_from_slice(v.as_bytes());305	}306	out.push(0);307	out.push(0);308	let v = out.as_ptr();309	std::mem::forget(out);310	v as *const c_char311}312313/// # Safety314#[no_mangle]315pub unsafe extern "C" fn jsonnet_evaluate_file_stream(316	vm: &State,317	filename: *const c_char,318	error: &mut c_int,319) -> *const c_char {320	let filename = parse_path(CStr::from_ptr(filename));321	match vm322		.import(&filename)323		.and_then(|v| vm.with_tla(v))324		.and_then(|v| vm.manifest_stream(v))325	{326		Ok(v) => {327			*error = 0;328			stream_to_raw(v)329		}330		Err(e) => {331			*error = 1;332			let out = vm.stringify_err(&e);333			CString::new(&out as &str)334				.expect("there should be no \\0 in the error string")335				.into_raw()336		}337	}338}339340/// # Safety341#[no_mangle]342pub unsafe extern "C" fn jsonnet_evaluate_snippet_stream(343	vm: &State,344	filename: *const c_char,345	snippet: *const c_char,346	error: &mut c_int,347) -> *const c_char {348	let filename = CStr::from_ptr(filename);349	let snippet = CStr::from_ptr(snippet);350	match vm351		.evaluate_snippet(352			filename.to_str().expect("filename is not utf-8"),353			snippet.to_str().expect("snippet is not utf-8"),354		)355		.and_then(|v| vm.with_tla(v))356		.and_then(|v| vm.manifest_stream(v))357	{358		Ok(v) => {359			*error = 0;360			stream_to_raw(v)361		}362		Err(e) => {363			*error = 1;364			let out = vm.stringify_err(&e);365			CString::new(&out as &str)366				.expect("there should be no \\0 in the error string")367				.into_raw()368		}369	}370}