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 16 17 18 19 fn file_trailing_newline(&self) -> bool {20 true21 }22}23impl<T> ManifestFormat for Box<T>24where25 T: ManifestFormat + ?Sized,26{27 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {28 let inner = &**self;29 inner.manifest_buf(val, buf)30 }31 fn file_trailing_newline(&self) -> bool {32 let inner = &**self;33 inner.file_trailing_newline()34 }35}36impl<T> ManifestFormat for &'_ T37where38 T: ManifestFormat + ?Sized,39{40 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {41 let inner = &**self;42 inner.manifest_buf(val, buf)43 }44 fn file_trailing_newline(&self) -> bool {45 let inner = &**self;46 inner.file_trailing_newline()47 }48}4950#[derive(PartialEq, Eq, Clone, Copy)]51enum JsonFormatting {52 53 Manifest,54 55 56 Std,57 58 ToString,59 60 Minify,61}6263pub struct JsonFormat<'s> {64 padding: Cow<'s, str>,65 mtype: JsonFormatting,66 newline: &'s str,67 key_val_sep: &'s str,68 #[cfg(feature = "exp-preserve-order")]69 preserve_order: bool,70}7172impl<'s> JsonFormat<'s> {73 74 pub fn minify(#[cfg(feature = "exp-preserve-order")] preserve_order: bool) -> Self {75 Self {76 padding: Cow::Borrowed(""),77 mtype: JsonFormatting::Minify,78 newline: "\n",79 key_val_sep: ":",80 #[cfg(feature = "exp-preserve-order")]81 preserve_order,82 }83 }84 85 pub fn std_to_string() -> Self {86 Self {87 padding: Cow::Borrowed(""),88 mtype: JsonFormatting::ToString,89 newline: "\n",90 key_val_sep: ": ",91 #[cfg(feature = "exp-preserve-order")]92 preserve_order: false,93 }94 }95 pub fn std_to_json(96 padding: String,97 newline: &'s str,98 key_val_sep: &'s str,99 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,100 ) -> Self {101 Self {102 padding: Cow::Owned(padding),103 mtype: JsonFormatting::Std,104 newline,105 key_val_sep,106 #[cfg(feature = "exp-preserve-order")]107 preserve_order,108 }109 }110 111 pub fn cli(112 padding: usize,113 #[cfg(feature = "exp-preserve-order")] preserve_order: bool,114 ) -> Self {115 if padding == 0 {116 return Self::minify(117 #[cfg(feature = "exp-preserve-order")]118 preserve_order,119 );120 }121 Self {122 padding: Cow::Owned(" ".repeat(padding)),123 mtype: JsonFormatting::Manifest,124 newline: "\n",125 key_val_sep: ": ",126 #[cfg(feature = "exp-preserve-order")]127 preserve_order,128 }129 }130}131impl Default for JsonFormat<'static> {132 fn default() -> Self {133 Self {134 padding: Cow::Borrowed(" "),135 mtype: JsonFormatting::Manifest,136 newline: "\n",137 key_val_sep: ": ",138 #[cfg(feature = "exp-preserve-order")]139 preserve_order: false,140 }141 }142}143144pub fn manifest_json_ex(val: &Val, options: &JsonFormat<'_>) -> Result<String> {145 let mut out = String::new();146 manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;147 Ok(out)148}149fn manifest_json_ex_buf(150 val: &Val,151 buf: &mut String,152 cur_padding: &mut String,153 options: &JsonFormat<'_>,154) -> Result<()> {155 let mtype = options.mtype;156 match val {157 Val::Bool(v) => {158 if *v {159 buf.push_str("true");160 } else {161 buf.push_str("false");162 }163 }164 Val::Null => buf.push_str("null"),165 Val::Str(s) => escape_string_json_buf(&s.clone().into_flat(), buf),166 Val::Num(n) => write!(buf, "{n}").unwrap(),167 #[cfg(feature = "exp-bigint")]168 Val::BigInt(n) => write!(buf, "{n}").unwrap(),169 Val::Arr(items) => {170 buf.push('[');171 if !items.is_empty() {172 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {173 buf.push_str(options.newline);174 }175176 let old_len = cur_padding.len();177 cur_padding.push_str(&options.padding);178 for (i, item) in items.iter().enumerate() {179 if i != 0 {180 buf.push(',');181 if mtype == JsonFormatting::ToString {182 buf.push(' ');183 } else if mtype != JsonFormatting::Minify {184 buf.push_str(options.newline);185 }186 }187 buf.push_str(cur_padding);188 manifest_json_ex_buf(&item?, buf, cur_padding, options)?;189 }190 cur_padding.truncate(old_len);191192 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {193 buf.push_str(options.newline);194 buf.push_str(cur_padding);195 }196 } else if mtype == JsonFormatting::Std {197 buf.push_str(options.newline);198 buf.push_str(options.newline);199 buf.push_str(cur_padding);200 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {201 buf.push(' ');202 }203 buf.push(']');204 }205 Val::Obj(obj) => {206 obj.run_assertions()?;207 buf.push('{');208 let fields = obj.fields(209 #[cfg(feature = "exp-preserve-order")]210 options.preserve_order,211 );212 if !fields.is_empty() {213 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {214 buf.push_str(options.newline);215 }216217 let old_len = cur_padding.len();218 cur_padding.push_str(&options.padding);219 for (i, field) in fields.into_iter().enumerate() {220 if i != 0 {221 buf.push(',');222 if mtype == JsonFormatting::ToString {223 buf.push(' ');224 } else if mtype != JsonFormatting::Minify {225 buf.push_str(options.newline);226 }227 }228 buf.push_str(cur_padding);229 escape_string_json_buf(&field, buf);230 buf.push_str(options.key_val_sep);231 State::push_description(232 || format!("field <{}> manifestification", field.clone()),233 || {234 let value = obj.get(field.clone())?.unwrap();235 manifest_json_ex_buf(&value, buf, cur_padding, options)?;236 Ok(())237 },238 )?;239 }240 cur_padding.truncate(old_len);241242 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {243 buf.push_str(options.newline);244 buf.push_str(cur_padding);245 }246 } else if mtype == JsonFormatting::Std {247 buf.push_str(options.newline);248 buf.push_str(options.newline);249 buf.push_str(cur_padding);250 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {251 buf.push(' ');252 }253 buf.push('}');254 }255 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),256 };257 Ok(())258}259260impl ManifestFormat for JsonFormat<'_> {261 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {262 manifest_json_ex_buf(&val, buf, &mut String::new(), self)263 }264}265266pub struct ToStringFormat;267impl ManifestFormat for ToStringFormat {268 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {269 JsonFormat::std_to_string().manifest_buf(val, out)270 }271 fn file_trailing_newline(&self) -> bool {272 false273 }274}275pub struct StringFormat;276impl ManifestFormat for StringFormat {277 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {278 let Val::Str(s) = val else {279 throw!("output should be string for string manifest format, got {}", val.value_type())280 };281 write!(out, "{s}").unwrap();282 Ok(())283 }284 fn file_trailing_newline(&self) -> bool {285 false286 }287}288289pub struct YamlStreamFormat<I>(pub I);290impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {291 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {292 let Val::Arr(arr) = val else {293 throw!("output should be array for yaml stream format, got {}", val.value_type())294 };295 if !arr.is_empty() {296 for v in arr.iter() {297 let v = v?;298 out.push_str("---\n");299 self.0.manifest_buf(v, out)?;300 out.push('\n');301 }302 out.push_str("...");303 }304 Ok(())305 }306}307308pub fn escape_string_json(s: &str) -> String {309 let mut buf = String::new();310 escape_string_json_buf(s, &mut buf);311 buf312}313314315316const BB: u8 = b'b'; 317const TT: u8 = b't'; 318const NN: u8 = b'n'; 319const FF: u8 = b'f'; 320const RR: u8 = b'r'; 321const QU: u8 = b'"'; 322const BS: u8 = b'\\'; 323const UU: u8 = b'u'; 324const __: u8 = 0;325326327328static ESCAPE: [u8; 256] = [329 330 UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, 331 UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, 332 __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, 333 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 334 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 335 __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, 336 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 337 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 338 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 339 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 340 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 341 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 342 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 343 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 344 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 345 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 346];347348pub fn escape_string_json_buf(value: &str, buf: &mut String) {349 350 let buf: &mut Vec<u8> = unsafe { &mut *(buf as *mut String).cast::<Vec<u8>>() };351 let bytes = value.as_bytes();352353 354 buf.reserve(value.len() + 2);355356 buf.push(b'"');357358 let mut start = 0;359360 for (i, &byte) in bytes.iter().enumerate() {361 let escape = ESCAPE[byte as usize];362 if escape == __ {363 continue;364 }365366 if start < i {367 buf.extend_from_slice(&bytes[start..i]);368 }369 start = i + 1;370371 match escape {372 self::BB | self::TT | self::NN | self::FF | self::RR | self::QU | self::BS => {373 buf.extend_from_slice(&[b'\\', escape]);374 }375 self::UU => {376 static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";377 let bytes = &[378 b'\\',379 b'u',380 b'0',381 b'0',382 HEX_DIGITS[(byte >> 4) as usize],383 HEX_DIGITS[(byte & 0xF) as usize],384 ];385 buf.extend_from_slice(bytes);386 }387 _ => unreachable!(),388 }389 }390391 if start == bytes.len() {392 buf.push(b'"');393 return;394 }395396 buf.extend_from_slice(&bytes[start..]);397 buf.push(b'"');398}