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

difftreelog

source

crates/jrsonnet-evaluator/src/builtin/manifest.rs7.0 KiBsourcehistory
1use crate::error::Error::*;2use crate::error::Result;3use crate::push_description_frame;4use crate::{throw, Val};56#[derive(PartialEq, Clone, Copy)]7pub enum ManifestType {8	// Applied in manifestification9	Manifest,10	/// Used for std.manifestJson11	/// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest12	Std,13	/// No line breaks, used in `obj+''`14	ToString,15	/// Minified json16	Minify,17}1819pub struct ManifestJsonOptions<'s> {20	pub padding: &'s str,21	pub mtype: ManifestType,22}2324pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {25	let mut out = String::new();26	manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;27	Ok(out)28}29fn manifest_json_ex_buf(30	val: &Val,31	buf: &mut String,32	cur_padding: &mut String,33	options: &ManifestJsonOptions<'_>,34) -> Result<()> {35	use std::fmt::Write;36	let mtype = options.mtype;37	match val {38		Val::Bool(v) => {39			if *v {40				buf.push_str("true");41			} else {42				buf.push_str("false");43			}44		}45		Val::Null => buf.push_str("null"),46		Val::Str(s) => escape_string_json_buf(s, buf),47		Val::Num(n) => write!(buf, "{}", n).unwrap(),48		Val::Arr(items) => {49			buf.push('[');50			if !items.is_empty() {51				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {52					buf.push('\n');53				}5455				let old_len = cur_padding.len();56				cur_padding.push_str(options.padding);57				for (i, item) in items.iter().enumerate() {58					if i != 0 {59						buf.push(',');60						if mtype == ManifestType::ToString {61							buf.push(' ');62						} else if mtype != ManifestType::Minify {63							buf.push('\n');64						}65					}66					buf.push_str(cur_padding);67					manifest_json_ex_buf(&item?, buf, cur_padding, options)?;68				}69				cur_padding.truncate(old_len);7071				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {72					buf.push('\n');73					buf.push_str(cur_padding);74				}75			} else if mtype == ManifestType::Std {76				buf.push_str("\n\n");77				buf.push_str(cur_padding);78			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {79				buf.push(' ');80			}81			buf.push(']');82		}83		Val::Obj(obj) => {84			obj.run_assertions()?;85			buf.push('{');86			let fields = obj.fields();87			if !fields.is_empty() {88				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {89					buf.push('\n');90				}9192				let old_len = cur_padding.len();93				cur_padding.push_str(options.padding);94				for (i, field) in fields.into_iter().enumerate() {95					if i != 0 {96						buf.push(',');97						if mtype == ManifestType::ToString {98							buf.push(' ');99						} else if mtype != ManifestType::Minify {100							buf.push('\n');101						}102					}103					buf.push_str(cur_padding);104					escape_string_json_buf(&field, buf);105					buf.push_str(": ");106					push_description_frame(107						|| format!("field <{}> manifestification", field.clone()),108						|| {109							let value = obj.get(field.clone())?.unwrap();110							manifest_json_ex_buf(&value, buf, cur_padding, options)?;111							Ok(Val::Null)112						},113					)?;114				}115				cur_padding.truncate(old_len);116117				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {118					buf.push('\n');119					buf.push_str(cur_padding);120				}121			} else if mtype == ManifestType::Std {122				buf.push_str("\n\n");123				buf.push_str(cur_padding);124			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {125				buf.push(' ');126			}127			buf.push('}');128		}129		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),130	};131	Ok(())132}133134pub fn escape_string_json(s: &str) -> String {135	let mut buf = String::new();136	escape_string_json_buf(s, &mut buf);137	buf138}139140fn escape_string_json_buf(s: &str, buf: &mut String) {141	use std::fmt::Write;142	buf.push('"');143	for c in s.chars() {144		match c {145			'"' => buf.push_str("\\\""),146			'\\' => buf.push_str("\\\\"),147			'\u{0008}' => buf.push_str("\\b"),148			'\u{000c}' => buf.push_str("\\f"),149			'\n' => buf.push_str("\\n"),150			'\r' => buf.push_str("\\r"),151			'\t' => buf.push_str("\\t"),152			c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {153				write!(buf, "\\u{:04x}", c as u32).unwrap()154			}155			c => buf.push(c),156		}157	}158	buf.push('"');159}160161pub struct ManifestYamlOptions<'s> {162	/// Padding before fields, i.e163	/// ```yaml164	/// a:165	///   b:166	/// ## <- this167	/// ```168	pub padding: &'s str,169	/// Padding before array elements in objects170	/// ```yaml171	/// a:172	///   - 1173	/// ## <- this174	/// ```175	pub arr_element_padding: &'s str,176}177178pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {179	let mut out = String::new();180	manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;181	Ok(out)182}183fn manifest_yaml_ex_buf(184	val: &Val,185	buf: &mut String,186	cur_padding: &mut String,187	options: &ManifestYamlOptions<'_>,188) -> Result<()> {189	use std::fmt::Write;190	match val {191		Val::Bool(v) => {192			if *v {193				buf.push_str("true")194			} else {195				buf.push_str("false")196			}197		}198		Val::Null => buf.push_str("null"),199		Val::Str(s) => {200			if s.is_empty() {201				buf.push_str("\"\"");202			} else if let Some(s) = s.strip_suffix('\n') {203				buf.push('|');204				for line in s.split('\n') {205					buf.push('\n');206					buf.push_str(options.padding);207					buf.push_str(line);208				}209			} else {210				escape_string_json_buf(s, buf)211			}212		}213		Val::Num(n) => write!(buf, "{}", *n).unwrap(),214		Val::Arr(a) => {215			if a.is_empty() {216				buf.push_str("[]");217			} else {218				for (i, item) in a.iter().enumerate() {219					if i != 0 {220						buf.push('\n');221						buf.push_str(cur_padding);222					}223					let item = item?;224					buf.push('-');225					match &item {226						Val::Arr(a) if !a.is_empty() => {227							buf.push('\n');228							buf.push_str(cur_padding);229							buf.push_str(options.padding);230						}231						_ => buf.push(' '),232					}233					let extra_padding = match &item {234						Val::Arr(a) => !a.is_empty(),235						Val::Obj(o) => !o.is_empty(),236						_ => false,237					};238					let prev_len = cur_padding.len();239					if extra_padding {240						cur_padding.push_str(options.padding);241					}242					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;243					cur_padding.truncate(prev_len);244				}245			}246		}247		Val::Obj(o) => {248			if o.is_empty() {249				buf.push_str("{}");250			} else {251				for (i, key) in o.fields().iter().enumerate() {252					if i != 0 {253						buf.push('\n');254						buf.push_str(cur_padding);255					}256					escape_string_json_buf(key, buf);257					buf.push(':');258					let prev_len = cur_padding.len();259					let item = o.get(key.clone())?.expect("field exists");260					match &item {261						Val::Arr(a) if !a.is_empty() => {262							buf.push('\n');263							buf.push_str(cur_padding);264							buf.push_str(options.arr_element_padding);265							cur_padding.push_str(options.arr_element_padding);266						}267						Val::Obj(o) if !o.is_empty() => {268							buf.push('\n');269							buf.push_str(cur_padding);270							buf.push_str(options.padding);271							cur_padding.push_str(options.padding);272						}273						_ => buf.push(' '),274					}275					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;276					cur_padding.truncate(prev_len);277				}278			}279		}280		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),281	}282	Ok(())283}