difftreelog
feat yaml stream output
in: master
5 files changed
bindings/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"),
}
crates/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(())
}
}
crates/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),
crates/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", "")
})
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use 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}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}