1use std::{borrow::Cow, fmt::Write};23use crate::{4 error::{ErrorKind::*, Result},5 throw, State, Val,6};78pub trait ManifestFormat {9 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()>;10 fn manifest(&self, val: Val) -> Result<String> {11 let mut out = String::new();12 self.manifest_buf(val, &mut out)?;13 Ok(out)14 }15}16impl<T> ManifestFormat for Box<T>17where18 T: ManifestFormat + ?Sized,19{20 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {21 let inner = &**self;22 inner.manifest_buf(val, buf)23 }24}25impl<T> ManifestFormat for &'_ T26where27 T: ManifestFormat + ?Sized,28{29 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {30 let inner = &**self;31 inner.manifest_buf(val, buf)32 }33}3435#[derive(PartialEq, Eq, Clone, Copy)]36enum JsonFormatting {37 38 Manifest,39 40 41 Std,42 43 ToString,44 45 Minify,46}4748pub struct JsonFormat<'s> {49 padding: Cow<'s, str>,50 mtype: JsonFormatting,51 newline: &'s str,52 key_val_sep: &'s str,53 #[cfg(feature = "exp-preserve-order")]54 preserve_order: bool,55}5657impl<'s> JsonFormat<'s> {58 59 pub fn minify(#[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Self {60 Self {61 padding: Cow::Borrowed(""),62 mtype: JsonFormatting::Minify,63 newline: "\n",64 key_val_sep: ":",65 #[cfg(feature = "exp-preserve-order")]66 preserve_order,67 }68 }69 70 pub fn std_to_string() -> Self {71 Self {72 padding: Cow::Borrowed(""),73 mtype: JsonFormatting::ToString,74 newline: "\n",75 key_val_sep: ": ",76 #[cfg(feature = "exp-preserve-order")]77 preserve_order: false,78 }79 }80 pub fn std_to_json(81 padding: String,82 newline: &'s str,83 key_val_sep: &'s str,84 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,85 ) -> Self {86 Self {87 padding: Cow::Owned(padding),88 mtype: JsonFormatting::Std,89 newline,90 key_val_sep,91 #[cfg(feature = "exp-preserve-order")]92 preserve_order,93 }94 }95 96 pub fn cli(97 padding: usize,98 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,99 ) -> Self {100 if padding == 0 {101 return Self::minify(102 #[cfg(feature = "exp-preserve-order")]103 preserve_order,104 );105 }106 Self {107 padding: Cow::Owned(" ".repeat(padding)),108 mtype: JsonFormatting::Manifest,109 newline: "\n",110 key_val_sep: ": ",111 #[cfg(feature = "exp-preserve-order")]112 preserve_order,113 }114 }115}116impl Default for JsonFormat<'static> {117 fn default() -> Self {118 Self {119 padding: Cow::Borrowed(" "),120 mtype: JsonFormatting::Manifest,121 newline: "\n",122 key_val_sep: ": ",123 #[cfg(feature = "exp-preserve-order")]124 preserve_order: false,125 }126 }127}128129pub fn manifest_json_ex(val: &Val, options: &JsonFormat<'_>) -> Result<String> {130 let mut out = String::new();131 manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;132 Ok(out)133}134fn manifest_json_ex_buf(135 val: &Val,136 buf: &mut String,137 cur_padding: &mut String,138 options: &JsonFormat<'_>,139) -> Result<()> {140 let mtype = options.mtype;141 match val {142 Val::Bool(v) => {143 if *v {144 buf.push_str("true");145 } else {146 buf.push_str("false");147 }148 }149 Val::Null => buf.push_str("null"),150 Val::Str(s) => escape_string_json_buf(&s.clone().into_flat(), buf),151 Val::Num(n) => write!(buf, "{n}").unwrap(),152 #[cfg(feature = "exp-bigint")]153 Val::BigInt(n) => write!(buf, "{n}").unwrap(),154 Val::Arr(items) => {155 buf.push('[');156 if !items.is_empty() {157 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {158 buf.push_str(options.newline);159 }160161 let old_len = cur_padding.len();162 cur_padding.push_str(&options.padding);163 for (i, item) in items.iter().enumerate() {164 if i != 0 {165 buf.push(',');166 if mtype == JsonFormatting::ToString {167 buf.push(' ');168 } else if mtype != JsonFormatting::Minify {169 buf.push_str(options.newline);170 }171 }172 buf.push_str(cur_padding);173 manifest_json_ex_buf(&item?, buf, cur_padding, options)?;174 }175 cur_padding.truncate(old_len);176177 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {178 buf.push_str(options.newline);179 buf.push_str(cur_padding);180 }181 } else if mtype == JsonFormatting::Std {182 buf.push_str(options.newline);183 buf.push_str(options.newline);184 buf.push_str(cur_padding);185 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {186 buf.push(' ');187 }188 buf.push(']');189 }190 Val::Obj(obj) => {191 obj.run_assertions()?;192 buf.push('{');193 let fields = obj.fields(194 #[cfg(feature = "exp-preserve-order")]195 options.preserve_order,196 );197 if !fields.is_empty() {198 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {199 buf.push_str(options.newline);200 }201202 let old_len = cur_padding.len();203 cur_padding.push_str(&options.padding);204 for (i, field) in fields.into_iter().enumerate() {205 if i != 0 {206 buf.push(',');207 if mtype == JsonFormatting::ToString {208 buf.push(' ');209 } else if mtype != JsonFormatting::Minify {210 buf.push_str(options.newline);211 }212 }213 buf.push_str(cur_padding);214 escape_string_json_buf(&field, buf);215 buf.push_str(options.key_val_sep);216 State::push_description(217 || format!("field <{}> manifestification", field.clone()),218 || {219 let value = obj.get(field.clone())?.unwrap();220 manifest_json_ex_buf(&value, buf, cur_padding, options)?;221 Ok(())222 },223 )?;224 }225 cur_padding.truncate(old_len);226227 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {228 buf.push_str(options.newline);229 buf.push_str(cur_padding);230 }231 } else if mtype == JsonFormatting::Std {232 buf.push_str(options.newline);233 buf.push_str(options.newline);234 buf.push_str(cur_padding);235 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {236 buf.push(' ');237 }238 buf.push('}');239 }240 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),241 };242 Ok(())243}244245impl ManifestFormat for JsonFormat<'_> {246 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {247 manifest_json_ex_buf(&val, buf, &mut String::new(), self)248 }249}250251pub struct ToStringFormat;252impl ManifestFormat for ToStringFormat {253 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {254 JsonFormat::std_to_string().manifest_buf(val, out)255 }256}257pub struct StringFormat;258impl ManifestFormat for StringFormat {259 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {260 let Val::Str(s) = val else {261 throw!("output should be string for string manifest format, got {}", val.value_type())262 };263 write!(out, "{s}").unwrap();264 Ok(())265 }266}267268pub struct YamlStreamFormat<I>(pub I);269impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {270 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {271 let Val::Arr(arr) = val else {272 throw!("output should be array for yaml stream format, got {}", val.value_type())273 };274 if !arr.is_empty() {275 for v in arr.iter() {276 let v = v?;277 out.push_str("---\n");278 self.0.manifest_buf(v, out)?;279 out.push('\n');280 }281 out.push_str("...");282 }283 Ok(())284 }285}286287pub fn escape_string_json(s: &str) -> String {288 let mut buf = String::new();289 escape_string_json_buf(s, &mut buf);290 buf291}292293294295const BB: u8 = b'b'; 296const TT: u8 = b't'; 297const NN: u8 = b'n'; 298const FF: u8 = b'f'; 299const RR: u8 = b'r'; 300const QU: u8 = b'"'; 301const BS: u8 = b'\\'; 302const UU: u8 = b'u'; 303const __: u8 = 0;304305306307static ESCAPE: [u8; 256] = [308 309 UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, 310 UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, 311 __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, 312 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 313 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 314 __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, 315 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 316 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 317 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 318 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 319 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 320 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 321 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 322 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 323 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 324 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 325];326327pub fn escape_string_json_buf(value: &str, buf: &mut String) {328 329 let buf: &mut Vec<u8> = unsafe { &mut *(buf as *mut String).cast::<Vec<u8>>() };330 let bytes = value.as_bytes();331332 333 buf.reserve(value.len() + 2);334335 buf.push(b'"');336337 let mut start = 0;338339 for (i, &byte) in bytes.iter().enumerate() {340 let escape = ESCAPE[byte as usize];341 if escape == __ {342 continue;343 }344345 if start < i {346 buf.extend_from_slice(&bytes[start..i]);347 }348 start = i + 1;349350 match escape {351 self::BB | self::TT | self::NN | self::FF | self::RR | self::QU | self::BS => {352 buf.extend_from_slice(&[b'\\', escape]);353 }354 self::UU => {355 static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";356 let bytes = &[357 b'\\',358 b'u',359 b'0',360 b'0',361 HEX_DIGITS[(byte >> 4) as usize],362 HEX_DIGITS[(byte & 0xF) as usize],363 ];364 buf.extend_from_slice(bytes);365 }366 _ => unreachable!(),367 }368 }369370 if start == bytes.len() {371 buf.push(b'"');372 return;373 }374375 buf.extend_from_slice(&bytes[start..]);376 buf.push(b'"');377}