git.delta.rocks / jrsonnet / refs/commits / 1feb056f19c3

difftreelog

feat yaml stream output

Lach2020-08-22parent: #ce6eeac.patch.diff
in: master

5 files changed

modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
before · bindings/jsonnet/src/lib.rs
1pub mod import;2pub mod interop;3pub mod val_extract;4pub mod val_make;5pub mod val_modify;6pub mod vars_tlas;78use import::NativeImportResolver;9use jrsonnet_evaluator::{EvaluationState, ManifestFormat, Val};10use std::{11	alloc::Layout,12	ffi::{CStr, CString},13	os::raw::{c_char, c_double, c_int, c_uint},14	path::PathBuf,15	rc::Rc,16};1718/// WASM stub19#[cfg(target_arch = "wasm32")]20#[no_mangle]21pub extern "C" fn _start() {}2223#[no_mangle]24pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {25	b"v0.16.0\0"26}2728#[no_mangle]29pub extern "C" fn jsonnet_make() -> *mut EvaluationState {30	let state = EvaluationState::default();31	state.with_stdlib();32	state.settings_mut().import_resolver = Box::new(NativeImportResolver::default());33	Box::into_raw(Box::new(state))34}3536/// # Safety37#[no_mangle]38#[allow(clippy::boxed_local)]39pub unsafe extern "C" fn jsonnet_destroy(vm: *mut EvaluationState) {40	Box::from_raw(vm);41}4243#[no_mangle]44pub extern "C" fn jsonnet_max_stack(vm: &EvaluationState, v: c_uint) {45	vm.settings_mut().max_stack = v as usize;46}4748// jrsonnet currently have no GC, so these functions is no-op49#[no_mangle]50pub extern "C" fn jsonnet_gc_min_objects(_vm: &EvaluationState, _v: c_uint) {}51#[no_mangle]52pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &EvaluationState, _v: c_double) {}5354#[no_mangle]55pub extern "C" fn jsonnet_string_output(vm: &EvaluationState, v: c_int) {56	match v {57		1 => vm.set_manifest_format(ManifestFormat::None),58		0 => vm.set_manifest_format(ManifestFormat::Json(4)),59		_ => panic!("incorrect output format"),60	}61}6263/// # Safety64///65/// This function is most definitely broken, but it works somehow, see TODO inside66#[no_mangle]67pub unsafe extern "C" fn jsonnet_realloc(68	_vm: &EvaluationState,69	buf: *mut u8,70	sz: usize,71) -> *mut u8 {72	if buf.is_null() {73		assert!(sz != 0);74		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());75	}76	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D77	// OR (Alternative way of fixing this TODO)78	// TODO: Standard allocator uses malloc, and it doesn't uses allocation size,79	// TODO: so it should work in normal cases. Maybe force allocator for this library?80	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();81	if sz == 0 {82		std::alloc::dealloc(buf, old_layout);83		return std::ptr::null_mut();84	}85	std::alloc::realloc(buf, old_layout, sz)86}8788/// # Safety89#[no_mangle]90#[allow(clippy::boxed_local)]91pub unsafe extern "C" fn jsonnet_json_destroy(_vm: &EvaluationState, v: *mut Val) {92	Box::from_raw(v);93}9495#[no_mangle]96pub extern "C" fn jsonnet_native_callback() {97	todo!()98}99100#[no_mangle]101pub extern "C" fn jsonnet_max_trace(vm: &EvaluationState, v: c_uint) {102	vm.set_max_trace(v as usize)103}104105/// # Safety106///107/// This function is safe, if received v is a pointer to normal C string108#[no_mangle]109pub unsafe extern "C" fn jsonnet_evaluate_file(110	vm: &EvaluationState,111	filename: *const c_char,112	error: &mut c_int,113) -> *const c_char {114	vm.run_in_state(|| {115		let filename = CStr::from_ptr(filename);116		match vm117			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))118			.and_then(|v| vm.with_tla(v))119			.and_then(|v| vm.manifest(v))120		{121			Ok(v) => {122				*error = 0;123				CString::new(&*v as &str).unwrap().into_raw()124			}125			Err(e) => {126				*error = 1;127				let out = vm.stringify_err(&e);128				CString::new(&out as &str).unwrap().into_raw()129			}130		}131	})132}133134/// # Safety135///136/// This function is safe, if received v is a pointer to normal C string137#[no_mangle]138pub unsafe extern "C" fn jsonnet_evaluate_snippet(139	vm: &EvaluationState,140	filename: *const c_char,141	snippet: *const c_char,142	error: &mut c_int,143) -> *const c_char {144	vm.run_in_state(|| {145		let filename = CStr::from_ptr(filename);146		let snippet = CStr::from_ptr(snippet);147		match vm148			.evaluate_snippet_raw(149				Rc::new(PathBuf::from(filename.to_str().unwrap())),150				snippet.to_str().unwrap().into(),151			)152			.and_then(|v| vm.with_tla(v))153			.and_then(|v| vm.manifest(v))154		{155			Ok(v) => {156				*error = 0;157				CString::new(&*v as &str).unwrap().into_raw()158			}159			Err(e) => {160				*error = 1;161				let out = vm.stringify_err(&e);162				CString::new(&out as &str).unwrap().into_raw()163			}164		}165	})166}167168#[no_mangle]169pub extern "C" fn jsonnet_evaluate_file_multi() {170	todo!()171}172#[no_mangle]173pub extern "C" fn jsonnet_evaluate_snippet_multi() {174	todo!()175}176#[no_mangle]177pub extern "C" fn jsonnet_evaluate_file_stream() {178	todo!()179}180#[no_mangle]181pub extern "C" fn jsonnet_evaluate_snippet_stream() {182	todo!()183}
after · bindings/jsonnet/src/lib.rs
1pub mod import;2pub mod interop;3pub mod val_extract;4pub mod val_make;5pub mod val_modify;6pub mod vars_tlas;78use import::NativeImportResolver;9use jrsonnet_evaluator::{EvaluationState, ManifestFormat, Val};10use std::{11	alloc::Layout,12	ffi::{CStr, CString},13	os::raw::{c_char, c_double, c_int, c_uint},14	path::PathBuf,15	rc::Rc,16};1718/// WASM stub19#[cfg(target_arch = "wasm32")]20#[no_mangle]21pub extern "C" fn _start() {}2223#[no_mangle]24pub extern "C" fn jsonnet_version() -> &'static [u8; 8] {25	b"v0.16.0\0"26}2728#[no_mangle]29pub extern "C" fn jsonnet_make() -> *mut EvaluationState {30	let state = EvaluationState::default();31	state.with_stdlib();32	state.settings_mut().import_resolver = Box::new(NativeImportResolver::default());33	Box::into_raw(Box::new(state))34}3536/// # Safety37#[no_mangle]38#[allow(clippy::boxed_local)]39pub unsafe extern "C" fn jsonnet_destroy(vm: *mut EvaluationState) {40	Box::from_raw(vm);41}4243#[no_mangle]44pub extern "C" fn jsonnet_max_stack(vm: &EvaluationState, v: c_uint) {45	vm.settings_mut().max_stack = v as usize;46}4748// jrsonnet currently have no GC, so these functions is no-op49#[no_mangle]50pub extern "C" fn jsonnet_gc_min_objects(_vm: &EvaluationState, _v: c_uint) {}51#[no_mangle]52pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &EvaluationState, _v: c_double) {}5354#[no_mangle]55pub extern "C" fn jsonnet_string_output(vm: &EvaluationState, v: c_int) {56	match v {57		1 => vm.set_manifest_format(ManifestFormat::String),58		0 => vm.set_manifest_format(ManifestFormat::Json(4)),59		_ => panic!("incorrect output format"),60	}61}6263/// # Safety64///65/// This function is most definitely broken, but it works somehow, see TODO inside66#[no_mangle]67pub unsafe extern "C" fn jsonnet_realloc(68	_vm: &EvaluationState,69	buf: *mut u8,70	sz: usize,71) -> *mut u8 {72	if buf.is_null() {73		assert!(sz != 0);74		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());75	}76	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D77	// OR (Alternative way of fixing this TODO)78	// TODO: Standard allocator uses malloc, and it doesn't uses allocation size,79	// TODO: so it should work in normal cases. Maybe force allocator for this library?80	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();81	if sz == 0 {82		std::alloc::dealloc(buf, old_layout);83		return std::ptr::null_mut();84	}85	std::alloc::realloc(buf, old_layout, sz)86}8788/// # Safety89#[no_mangle]90#[allow(clippy::boxed_local)]91pub unsafe extern "C" fn jsonnet_json_destroy(_vm: &EvaluationState, v: *mut Val) {92	Box::from_raw(v);93}9495#[no_mangle]96pub extern "C" fn jsonnet_native_callback() {97	todo!()98}99100#[no_mangle]101pub extern "C" fn jsonnet_max_trace(vm: &EvaluationState, v: c_uint) {102	vm.set_max_trace(v as usize)103}104105/// # Safety106///107/// This function is safe, if received v is a pointer to normal C string108#[no_mangle]109pub unsafe extern "C" fn jsonnet_evaluate_file(110	vm: &EvaluationState,111	filename: *const c_char,112	error: &mut c_int,113) -> *const c_char {114	vm.run_in_state(|| {115		let filename = CStr::from_ptr(filename);116		match vm117			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))118			.and_then(|v| vm.with_tla(v))119			.and_then(|v| vm.manifest(v))120		{121			Ok(v) => {122				*error = 0;123				CString::new(&*v as &str).unwrap().into_raw()124			}125			Err(e) => {126				*error = 1;127				let out = vm.stringify_err(&e);128				CString::new(&out as &str).unwrap().into_raw()129			}130		}131	})132}133134/// # Safety135///136/// This function is safe, if received v is a pointer to normal C string137#[no_mangle]138pub unsafe extern "C" fn jsonnet_evaluate_snippet(139	vm: &EvaluationState,140	filename: *const c_char,141	snippet: *const c_char,142	error: &mut c_int,143) -> *const c_char {144	vm.run_in_state(|| {145		let filename = CStr::from_ptr(filename);146		let snippet = CStr::from_ptr(snippet);147		match vm148			.evaluate_snippet_raw(149				Rc::new(PathBuf::from(filename.to_str().unwrap())),150				snippet.to_str().unwrap().into(),151			)152			.and_then(|v| vm.with_tla(v))153			.and_then(|v| vm.manifest(v))154		{155			Ok(v) => {156				*error = 0;157				CString::new(&*v as &str).unwrap().into_raw()158			}159			Err(e) => {160				*error = 1;161				let out = vm.stringify_err(&e);162				CString::new(&out as &str).unwrap().into_raw()163			}164		}165	})166}167168#[no_mangle]169pub extern "C" fn jsonnet_evaluate_file_multi() {170	todo!()171}172#[no_mangle]173pub extern "C" fn jsonnet_evaluate_snippet_multi() {174	todo!()175}176#[no_mangle]177pub extern "C" fn jsonnet_evaluate_file_stream() {178	todo!()179}180#[no_mangle]181pub extern "C" fn jsonnet_evaluate_snippet_stream() {182	todo!()183}
modifiedcrates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/manifest.rs
+++ b/crates/jrsonnet-cli/src/manifest.rs
@@ -5,7 +5,7 @@
 
 pub enum ManifestFormatName {
 	/// Expect string as output, and write them directly
-	None,
+	String,
 	Json,
 	Yaml,
 }
@@ -14,7 +14,7 @@
 	type Err = &'static str;
 	fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
 		Ok(match s {
-			"none" => ManifestFormatName::None,
+			"string" => ManifestFormatName::String,
 			"json" => ManifestFormatName::Json,
 			"yaml" => ManifestFormatName::Yaml,
 			_ => return Err("no such format"),
@@ -27,14 +27,17 @@
 // #[clap(group = clap::ArgGroup::new("output_format"), help_heading = "MANIFESTIFICATION OUTPUT")]
 pub struct ManifestOpts {
 	/// Output format, wraps resulting value to corresponding std.manifest call.
-	/// If none - then jsonnet file is expected to return plain string value, otherwise
+	/// If string - then jsonnet file is expected to return plain string value, otherwise
 	/// output will be serialized to specified format
-	#[clap(long, short = 'f', default_value = "json", possible_values = &["none", "json", "yaml"]/*, group = "output_format"*/)]
+	#[clap(long, short = 'f', default_value = "json", possible_values = &["string", "json", "yaml"]/*, group = "output_format"*/)]
 	format: ManifestFormatName,
 	/// Expect string as output, and write them directly.
-	/// Shortcut for --format=none, and can't be set with format together
+	/// Shortcut for --format=string, and can't be set with format together
 	#[clap(long, short = 'S'/*, group = "output_format"*/)]
 	string: bool,
+	/// Write output as YAML stream, can be used with --format json/yaml
+	#[clap(long, short = 'y')]
+	yaml_stream: bool,
 	/// Numbed of spaces to pad output manifest with.
 	/// 0 for hard tabs, -1 for single line output
 	#[clap(long, default_value = "3")]
@@ -43,10 +46,10 @@
 impl ConfigureState for ManifestOpts {
 	fn configure(&self, state: &EvaluationState) -> Result<()> {
 		if self.string {
-			state.set_manifest_format(ManifestFormat::None);
+			state.set_manifest_format(ManifestFormat::String);
 		} else {
 			match self.format {
-				ManifestFormatName::None => state.set_manifest_format(ManifestFormat::None),
+				ManifestFormatName::String => state.set_manifest_format(ManifestFormat::String),
 				ManifestFormatName::Json => {
 					state.set_manifest_format(ManifestFormat::Json(self.line_padding))
 				}
@@ -55,6 +58,11 @@
 				}
 			}
 		}
+		if self.yaml_stream {
+			state.set_manifest_format(ManifestFormat::YamlStream(Box::new(
+				state.manifest_format(),
+			)))
+		}
 		Ok(())
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -58,6 +58,11 @@
 	DivisionByZero,
 
 	StringManifestOutputIsNotAString,
+	StreamManifestOutputIsNotAArray,
+	MultiManifestOutputIsNotAObject,
+
+	StreamManifestOutputCannotBeRecursed,
+	StreamManifestCannotNestString,
 
 	ImportCallbackError(String),
 	InvalidUnicodeCodepointGot(u32),
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -53,13 +53,6 @@
 	}
 }
 
-#[derive(Clone)]
-pub enum ManifestFormat {
-	Yaml(usize),
-	Json(usize),
-	None,
-}
-
 pub struct EvaluationSettings {
 	/// Limits recursion by limiting stack frames
 	pub max_stack: usize,
@@ -321,16 +314,7 @@
 	}
 
 	pub fn manifest(&self, val: Val) -> Result<Rc<str>> {
-		self.run_in_state(|| {
-			Ok(match self.manifest_format() {
-				ManifestFormat::Yaml(padding) => val.into_yaml(padding)?,
-				ManifestFormat::Json(padding) => val.into_json(padding)?,
-				ManifestFormat::None => match val {
-					Val::Str(s) => s,
-					_ => throw!(StringManifestOutputIsNotAString),
-				},
-			})
-		})
+		self.run_in_state(|| val.manifest(&self.manifest_format()))
 	}
 
 	/// If passed value is function - call with set TLA
@@ -521,7 +505,7 @@
 				evaluator
 					.evaluate_snippet_raw(Rc::new(PathBuf::from("raw.jsonnet")), $str.into())
 					.unwrap()
-					.into_json(0)
+					.to_json(0)
 					.unwrap()
 					.replace("\n", "")
 				})
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -131,6 +131,14 @@
 	}
 }
 
+#[derive(Clone)]
+pub enum ManifestFormat {
+	YamlStream(Box<ManifestFormat>),
+	Yaml(usize),
+	Json(usize),
+	String,
+}
+
 #[derive(Debug, Clone)]
 pub enum Val {
 	Bool(bool),
@@ -221,10 +229,46 @@
 		})
 	}
 
+
+	pub fn manifest(&self, ty: &ManifestFormat) -> Result<Rc<str>> {
+		Ok(match ty {
+			ManifestFormat::YamlStream(format) => {
+				let arr = match self {
+					Val::Arr(a) => a,
+					_ => throw!(StreamManifestOutputIsNotAArray),
+				};
+				let mut out = String::new();
+
+				match format as &ManifestFormat {
+					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),
+					ManifestFormat::String => throw!(StreamManifestCannotNestString),
+					_ => {}
+				};
+
+				if !arr.is_empty() {
+					for v in arr.iter() {
+						out.push_str("---\n");
+						out.push_str(&v.manifest(format)?);
+						out.push_str("\n");
+					}
+					out.push_str("...");
+				}
+
+				out.into()
+			}
+			ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,
+			ManifestFormat::Json(padding) => self.to_json(*padding)?,
+			ManifestFormat::String => match self {
+				Val::Str(s) => s.clone(),
+				_ => throw!(StringManifestOutputIsNotAString),
+			},
+		})
+	}
+
 	/// For manifestification
-	pub fn into_json(self, padding: usize) -> Result<Rc<str>> {
+	pub fn to_json(&self, padding: usize) -> Result<Rc<str>> {
 		manifest_json_ex(
-			&self,
+			self,
 			&ManifestJsonOptions {
 				padding: &" ".repeat(padding),
 				mtype: if padding == 0 {
@@ -239,7 +283,7 @@
 
 	/// Calls std.manifestJson
 	#[cfg(feature = "faster")]
-	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {
+	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {
 		manifest_json_ex(
 			&self,
 			&ManifestJsonOptions {
@@ -252,11 +296,11 @@
 
 	/// Calls std.manifestJson
 	#[cfg(not(feature = "faster"))]
-	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {
+	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {
 		with_state(|s| {
 			let ctx = s
 				.create_default_context()?
-				.with_var("__tmp__to_json__".into(), self)?;
+				.with_var("__tmp__to_json__".into(), self.clone())?;
 			Ok(evaluate(
 				ctx,
 				&el!(Expr::Apply(
@@ -274,11 +318,11 @@
 			.try_cast_str("to json")?)
 		})
 	}
-	pub fn into_yaml(self, padding: usize) -> Result<Rc<str>> {
+	pub fn to_yaml(&self, padding: usize) -> Result<Rc<str>> {
 		with_state(|s| {
 			let ctx = s
 				.create_default_context()?
-				.with_var("__tmp__to_json__".into(), self);
+				.with_var("__tmp__to_json__".into(), self.clone());
 			Ok(evaluate(
 				ctx,
 				&el!(Expr::Apply(