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
--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -54,7 +54,7 @@
 #[no_mangle]
 pub extern "C" fn jsonnet_string_output(vm: &EvaluationState, v: c_int) {
 	match v {
-		1 => vm.set_manifest_format(ManifestFormat::None),
+		1 => vm.set_manifest_format(ManifestFormat::String),
 		0 => vm.set_manifest_format(ManifestFormat::Json(4)),
 		_ => panic!("incorrect output format"),
 	}
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
before · crates/jrsonnet-evaluator/src/val.rs
1use crate::{2	builtin::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},3	error::Error::*,4	evaluate,5	function::{parse_function_call, parse_function_call_map, place_args},6	throw, with_state, Context, ObjValue, Result,7};8use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};9use std::{10	cell::RefCell,11	collections::HashMap,12	fmt::{Debug, Display},13	rc::Rc,14};1516enum LazyValInternals {17	Computed(Val),18	Waiting(Box<dyn Fn() -> Result<Val>>),19}20#[derive(Clone)]21pub struct LazyVal(Rc<RefCell<LazyValInternals>>);22impl LazyVal {23	pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {24		LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))25	}26	pub fn new_resolved(val: Val) -> Self {27		LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))28	}29	pub fn evaluate(&self) -> Result<Val> {30		let new_value = match &*self.0.borrow() {31			LazyValInternals::Computed(v) => return Ok(v.clone()),32			LazyValInternals::Waiting(f) => f()?,33		};34		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());35		Ok(new_value)36	}37}3839#[macro_export]40macro_rules! lazy_val {41	($f: expr) => {42		$crate::LazyVal::new(Box::new($f))43	};44}45#[macro_export]46macro_rules! resolved_lazy_val {47	($f: expr) => {48		$crate::LazyVal::new_resolved($f)49	};50}51impl Debug for LazyVal {52	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {53		write!(f, "Lazy")54	}55}56impl PartialEq for LazyVal {57	fn eq(&self, other: &Self) -> bool {58		Rc::ptr_eq(&self.0, &other.0)59	}60}6162#[derive(Debug, PartialEq)]63pub struct FuncDesc {64	pub name: Rc<str>,65	pub ctx: Context,66	pub params: ParamsDesc,67	pub body: LocExpr,68}69impl FuncDesc {70	/// This function is always inlined to make tailstrict work71	pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {72		let ctx = parse_function_call(73			call_ctx,74			Some(self.ctx.clone()),75			&self.params,76			args,77			tailstrict,78		)?;79		evaluate(ctx, &self.body)80	}8182	pub fn evaluate_map(83		&self,84		call_ctx: Context,85		args: &HashMap<Rc<str>, Val>,86		tailstrict: bool,87	) -> Result<Val> {88		let ctx = parse_function_call_map(89			call_ctx,90			Some(self.ctx.clone()),91			&self.params,92			args,93			tailstrict,94		)?;95		evaluate(ctx, &self.body)96	}9798	pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {99		let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;100		evaluate(ctx, &self.body)101	}102}103104#[derive(Debug, Clone, Copy, PartialEq)]105pub enum ValType {106	Bool,107	Null,108	Str,109	Num,110	Arr,111	Obj,112	Func,113}114impl ValType {115	pub fn name(&self) -> &'static str {116		use ValType::*;117		match self {118			Bool => "boolean",119			Null => "null",120			Str => "string",121			Num => "number",122			Arr => "array",123			Obj => "object",124			Func => "function",125		}126	}127}128impl Display for ValType {129	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {130		write!(f, "{}", self.name())131	}132}133134#[derive(Debug, Clone)]135pub enum Val {136	Bool(bool),137	Null,138	Str(Rc<str>),139	Num(f64),140	Lazy(LazyVal),141	Arr(Rc<Vec<Val>>),142	Obj(ObjValue),143	Func(Rc<FuncDesc>),144145	// Library functions implemented in native146	Intristic(Rc<str>, Rc<str>),147}148macro_rules! matches_unwrap {149	($e: expr, $p: pat, $r: expr) => {150		match $e {151			$p => $r,152			_ => panic!("no match"),153			}154	};155}156impl Val {157	/// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity158	pub fn new_checked_num(num: f64) -> Result<Val> {159		if num.is_finite() {160			Ok(Val::Num(num))161		} else {162			throw!(RuntimeError("overflow".into()))163		}164	}165166	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {167		let this_type = self.value_type()?;168		if this_type != val_type {169			throw!(TypeMismatch(context, vec![val_type], this_type))170		} else {171			Ok(())172		}173	}174	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {175		self.assert_type(context, ValType::Bool)?;176		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))177	}178	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {179		self.assert_type(context, ValType::Str)?;180		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))181	}182	pub fn try_cast_num(self, context: &'static str) -> Result<f64> {183		self.assert_type(context, ValType::Num)?;184		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))185	}186	pub fn unwrap_if_lazy(&self) -> Result<Self> {187		Ok(if let Val::Lazy(v) = self {188			v.evaluate()?.unwrap_if_lazy()?189		} else {190			self.clone()191		})192	}193	pub fn value_type(&self) -> Result<ValType> {194		Ok(match self {195			Val::Str(..) => ValType::Str,196			Val::Num(..) => ValType::Num,197			Val::Arr(..) => ValType::Arr,198			Val::Obj(..) => ValType::Obj,199			Val::Func(..) => ValType::Func,200			Val::Bool(_) => ValType::Bool,201			Val::Null => ValType::Null,202			Val::Intristic(_, _) => ValType::Func,203			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,204		})205	}206207	pub fn into_string(self) -> Result<Rc<str>> {208		Ok(match self.unwrap_if_lazy()? {209			Val::Bool(true) => "true".into(),210			Val::Bool(false) => "false".into(),211			Val::Null => "null".into(),212			Val::Str(s) => s,213			v => manifest_json_ex(214				&v,215				&ManifestJsonOptions {216					padding: &"",217					mtype: ManifestType::ToString,218				},219			)?220			.into(),221		})222	}223224	/// For manifestification225	pub fn into_json(self, padding: usize) -> Result<Rc<str>> {226		manifest_json_ex(227			&self,228			&ManifestJsonOptions {229				padding: &" ".repeat(padding),230				mtype: if padding == 0 {231					ManifestType::Minify232				} else {233					ManifestType::Manifest234				},235			},236		)237		.map(|s| s.into())238	}239240	/// Calls std.manifestJson241	#[cfg(feature = "faster")]242	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {243		manifest_json_ex(244			&self,245			&ManifestJsonOptions {246				padding: &" ".repeat(padding),247				mtype: ManifestType::Std,248			},249		)250		.map(|s| s.into())251	}252253	/// Calls std.manifestJson254	#[cfg(not(feature = "faster"))]255	pub fn into_std_json(self, padding: usize) -> Result<Rc<str>> {256		with_state(|s| {257			let ctx = s258				.create_default_context()?259				.with_var("__tmp__to_json__".into(), self)?;260			Ok(evaluate(261				ctx,262				&el!(Expr::Apply(263					el!(Expr::Index(264						el!(Expr::Var("std".into())),265						el!(Expr::Str("manifestJsonEx".into()))266					)),267					ArgsDesc(vec![268						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),269						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))270					]),271					false272				)),273			)?274			.try_cast_str("to json")?)275		})276	}277	pub fn into_yaml(self, padding: usize) -> Result<Rc<str>> {278		with_state(|s| {279			let ctx = s280				.create_default_context()?281				.with_var("__tmp__to_json__".into(), self);282			Ok(evaluate(283				ctx,284				&el!(Expr::Apply(285					el!(Expr::Index(286						el!(Expr::Var("std".into())),287						el!(Expr::Str("manifestYamlDoc".into()))288					)),289					ArgsDesc(vec![290						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),291						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))292					]),293					false294				)),295			)?296			.try_cast_str("to json")?)297		})298	}299}300301fn is_function_like(val: &Val) -> bool {302	matches!(val, Val::Func(_) | Val::Intristic(_, _))303}304305/// Implements std.primitiveEquals builtin306pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {307	Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {308		(Val::Bool(a), Val::Bool(b)) => a == b,309		(Val::Null, Val::Null) => true,310		(Val::Str(a), Val::Str(b)) => a == b,311		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,312		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(313			"primitiveEquals operates on primitive types, got array".into(),314		)),315		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(316			"primitiveEquals operates on primitive types, got object".into(),317		)),318		(a, b) if is_function_like(&a) && is_function_like(&b) => {319			throw!(RuntimeError("cannot test equality of functions".into()))320		}321		(_, _) => false,322	})323}324325/// Native implementation of std.equals326pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {327	let val_a = val_a.unwrap_if_lazy()?;328	let val_b = val_b.unwrap_if_lazy()?;329330	if val_a.value_type()? != val_b.value_type()? {331		return Ok(false);332	}333	match (val_a, val_b) {334		// Cant test for ptr equality, because all fields needs to be evaluated335		(Val::Arr(a), Val::Arr(b)) => {336			if a.len() != b.len() {337				return Ok(false);338			}339			for (a, b) in a.iter().zip(b.iter()) {340				if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {341					return Ok(false);342				}343			}344			Ok(true)345		}346		(Val::Obj(a), Val::Obj(b)) => {347			let fields = a.visible_fields();348			if fields != b.visible_fields() {349				return Ok(false);350			}351			for field in fields {352				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {353					return Ok(false);354				}355			}356			Ok(true)357		}358		(a, b) => Ok(primitive_equals(&a, &b)?),359	}360}
after · crates/jrsonnet-evaluator/src/val.rs
1use crate::{2	builtin::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},3	error::Error::*,4	evaluate,5	function::{parse_function_call, parse_function_call_map, place_args},6	throw, with_state, Context, ObjValue, Result,7};8use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};9use std::{10	cell::RefCell,11	collections::HashMap,12	fmt::{Debug, Display},13	rc::Rc,14};1516enum LazyValInternals {17	Computed(Val),18	Waiting(Box<dyn Fn() -> Result<Val>>),19}20#[derive(Clone)]21pub struct LazyVal(Rc<RefCell<LazyValInternals>>);22impl LazyVal {23	pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {24		LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))25	}26	pub fn new_resolved(val: Val) -> Self {27		LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))28	}29	pub fn evaluate(&self) -> Result<Val> {30		let new_value = match &*self.0.borrow() {31			LazyValInternals::Computed(v) => return Ok(v.clone()),32			LazyValInternals::Waiting(f) => f()?,33		};34		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());35		Ok(new_value)36	}37}3839#[macro_export]40macro_rules! lazy_val {41	($f: expr) => {42		$crate::LazyVal::new(Box::new($f))43	};44}45#[macro_export]46macro_rules! resolved_lazy_val {47	($f: expr) => {48		$crate::LazyVal::new_resolved($f)49	};50}51impl Debug for LazyVal {52	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {53		write!(f, "Lazy")54	}55}56impl PartialEq for LazyVal {57	fn eq(&self, other: &Self) -> bool {58		Rc::ptr_eq(&self.0, &other.0)59	}60}6162#[derive(Debug, PartialEq)]63pub struct FuncDesc {64	pub name: Rc<str>,65	pub ctx: Context,66	pub params: ParamsDesc,67	pub body: LocExpr,68}69impl FuncDesc {70	/// This function is always inlined to make tailstrict work71	pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {72		let ctx = parse_function_call(73			call_ctx,74			Some(self.ctx.clone()),75			&self.params,76			args,77			tailstrict,78		)?;79		evaluate(ctx, &self.body)80	}8182	pub fn evaluate_map(83		&self,84		call_ctx: Context,85		args: &HashMap<Rc<str>, Val>,86		tailstrict: bool,87	) -> Result<Val> {88		let ctx = parse_function_call_map(89			call_ctx,90			Some(self.ctx.clone()),91			&self.params,92			args,93			tailstrict,94		)?;95		evaluate(ctx, &self.body)96	}9798	pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {99		let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;100		evaluate(ctx, &self.body)101	}102}103104#[derive(Debug, Clone, Copy, PartialEq)]105pub enum ValType {106	Bool,107	Null,108	Str,109	Num,110	Arr,111	Obj,112	Func,113}114impl ValType {115	pub fn name(&self) -> &'static str {116		use ValType::*;117		match self {118			Bool => "boolean",119			Null => "null",120			Str => "string",121			Num => "number",122			Arr => "array",123			Obj => "object",124			Func => "function",125		}126	}127}128impl Display for ValType {129	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {130		write!(f, "{}", self.name())131	}132}133134#[derive(Clone)]135pub enum ManifestFormat {136	YamlStream(Box<ManifestFormat>),137	Yaml(usize),138	Json(usize),139	String,140}141142#[derive(Debug, Clone)]143pub enum Val {144	Bool(bool),145	Null,146	Str(Rc<str>),147	Num(f64),148	Lazy(LazyVal),149	Arr(Rc<Vec<Val>>),150	Obj(ObjValue),151	Func(Rc<FuncDesc>),152153	// Library functions implemented in native154	Intristic(Rc<str>, Rc<str>),155}156macro_rules! matches_unwrap {157	($e: expr, $p: pat, $r: expr) => {158		match $e {159			$p => $r,160			_ => panic!("no match"),161			}162	};163}164impl Val {165	/// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity166	pub fn new_checked_num(num: f64) -> Result<Val> {167		if num.is_finite() {168			Ok(Val::Num(num))169		} else {170			throw!(RuntimeError("overflow".into()))171		}172	}173174	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {175		let this_type = self.value_type()?;176		if this_type != val_type {177			throw!(TypeMismatch(context, vec![val_type], this_type))178		} else {179			Ok(())180		}181	}182	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {183		self.assert_type(context, ValType::Bool)?;184		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))185	}186	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {187		self.assert_type(context, ValType::Str)?;188		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))189	}190	pub fn try_cast_num(self, context: &'static str) -> Result<f64> {191		self.assert_type(context, ValType::Num)?;192		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))193	}194	pub fn unwrap_if_lazy(&self) -> Result<Self> {195		Ok(if let Val::Lazy(v) = self {196			v.evaluate()?.unwrap_if_lazy()?197		} else {198			self.clone()199		})200	}201	pub fn value_type(&self) -> Result<ValType> {202		Ok(match self {203			Val::Str(..) => ValType::Str,204			Val::Num(..) => ValType::Num,205			Val::Arr(..) => ValType::Arr,206			Val::Obj(..) => ValType::Obj,207			Val::Func(..) => ValType::Func,208			Val::Bool(_) => ValType::Bool,209			Val::Null => ValType::Null,210			Val::Intristic(_, _) => ValType::Func,211			Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,212		})213	}214215	pub fn into_string(self) -> Result<Rc<str>> {216		Ok(match self.unwrap_if_lazy()? {217			Val::Bool(true) => "true".into(),218			Val::Bool(false) => "false".into(),219			Val::Null => "null".into(),220			Val::Str(s) => s,221			v => manifest_json_ex(222				&v,223				&ManifestJsonOptions {224					padding: &"",225					mtype: ManifestType::ToString,226				},227			)?228			.into(),229		})230	}231232233	pub fn manifest(&self, ty: &ManifestFormat) -> Result<Rc<str>> {234		Ok(match ty {235			ManifestFormat::YamlStream(format) => {236				let arr = match self {237					Val::Arr(a) => a,238					_ => throw!(StreamManifestOutputIsNotAArray),239				};240				let mut out = String::new();241242				match format as &ManifestFormat {243					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),244					ManifestFormat::String => throw!(StreamManifestCannotNestString),245					_ => {}246				};247248				if !arr.is_empty() {249					for v in arr.iter() {250						out.push_str("---\n");251						out.push_str(&v.manifest(format)?);252						out.push_str("\n");253					}254					out.push_str("...");255				}256257				out.into()258			}259			ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,260			ManifestFormat::Json(padding) => self.to_json(*padding)?,261			ManifestFormat::String => match self {262				Val::Str(s) => s.clone(),263				_ => throw!(StringManifestOutputIsNotAString),264			},265		})266	}267268	/// For manifestification269	pub fn to_json(&self, padding: usize) -> Result<Rc<str>> {270		manifest_json_ex(271			self,272			&ManifestJsonOptions {273				padding: &" ".repeat(padding),274				mtype: if padding == 0 {275					ManifestType::Minify276				} else {277					ManifestType::Manifest278				},279			},280		)281		.map(|s| s.into())282	}283284	/// Calls std.manifestJson285	#[cfg(feature = "faster")]286	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {287		manifest_json_ex(288			&self,289			&ManifestJsonOptions {290				padding: &" ".repeat(padding),291				mtype: ManifestType::Std,292			},293		)294		.map(|s| s.into())295	}296297	/// Calls std.manifestJson298	#[cfg(not(feature = "faster"))]299	pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {300		with_state(|s| {301			let ctx = s302				.create_default_context()?303				.with_var("__tmp__to_json__".into(), self.clone())?;304			Ok(evaluate(305				ctx,306				&el!(Expr::Apply(307					el!(Expr::Index(308						el!(Expr::Var("std".into())),309						el!(Expr::Str("manifestJsonEx".into()))310					)),311					ArgsDesc(vec![312						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),313						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))314					]),315					false316				)),317			)?318			.try_cast_str("to json")?)319		})320	}321	pub fn to_yaml(&self, padding: usize) -> Result<Rc<str>> {322		with_state(|s| {323			let ctx = s324				.create_default_context()?325				.with_var("__tmp__to_json__".into(), self.clone());326			Ok(evaluate(327				ctx,328				&el!(Expr::Apply(329					el!(Expr::Index(330						el!(Expr::Var("std".into())),331						el!(Expr::Str("manifestYamlDoc".into()))332					)),333					ArgsDesc(vec![334						Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),335						Arg(None, el!(Expr::Str(" ".repeat(padding).into())))336					]),337					false338				)),339			)?340			.try_cast_str("to json")?)341		})342	}343}344345fn is_function_like(val: &Val) -> bool {346	matches!(val, Val::Func(_) | Val::Intristic(_, _))347}348349/// Implements std.primitiveEquals builtin350pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {351	Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {352		(Val::Bool(a), Val::Bool(b)) => a == b,353		(Val::Null, Val::Null) => true,354		(Val::Str(a), Val::Str(b)) => a == b,355		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,356		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(357			"primitiveEquals operates on primitive types, got array".into(),358		)),359		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(360			"primitiveEquals operates on primitive types, got object".into(),361		)),362		(a, b) if is_function_like(&a) && is_function_like(&b) => {363			throw!(RuntimeError("cannot test equality of functions".into()))364		}365		(_, _) => false,366	})367}368369/// Native implementation of std.equals370pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {371	let val_a = val_a.unwrap_if_lazy()?;372	let val_b = val_b.unwrap_if_lazy()?;373374	if val_a.value_type()? != val_b.value_type()? {375		return Ok(false);376	}377	match (val_a, val_b) {378		// Cant test for ptr equality, because all fields needs to be evaluated379		(Val::Arr(a), Val::Arr(b)) => {380			if a.len() != b.len() {381				return Ok(false);382			}383			for (a, b) in a.iter().zip(b.iter()) {384				if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {385					return Ok(false);386				}387			}388			Ok(true)389		}390		(Val::Obj(a), Val::Obj(b)) => {391			let fields = a.visible_fields();392			if fields != b.visible_fields() {393				return Ok(false);394			}395			for field in fields {396				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {397					return Ok(false);398				}399			}400			Ok(true)401		}402		(a, b) => Ok(primitive_equals(&a, &b)?),403	}404}