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!(280 "output should be string for string manifest format, got {}",281 val.value_type()282 )283 };284 write!(out, "{s}").unwrap();285 Ok(())286 }287 fn file_trailing_newline(&self) -> bool {288 false289 }290}291292pub struct YamlStreamFormat<I>(pub I);293impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {294 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {295 let Val::Arr(arr) = val else {296 throw!(297 "output should be array for yaml stream format, got {}",298 val.value_type()299 )300 };301 if !arr.is_empty() {302 for v in arr.iter() {303 let v = v?;304 out.push_str("---\n");305 self.0.manifest_buf(v, out)?;306 out.push('\n');307 }308 out.push_str("...");309 }310 Ok(())311 }312}313314pub fn escape_string_json(s: &str) -> String {315 let mut buf = String::new();316 escape_string_json_buf(s, &mut buf);317 buf318}319320321322const BB: u8 = b'b'; 323const TT: u8 = b't'; 324const NN: u8 = b'n'; 325const FF: u8 = b'f'; 326const RR: u8 = b'r'; 327const QU: u8 = b'"'; 328const BS: u8 = b'\\'; 329const UU: u8 = b'u'; 330const __: u8 = 0;331332333334static ESCAPE: [u8; 256] = [335 336 UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, 337 UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, 338 __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, 339 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 340 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 341 __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, 342 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 343 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 344 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 345 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 346 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 347 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 348 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 349 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 350 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 351 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 352];353354pub fn escape_string_json_buf(value: &str, buf: &mut String) {355 356 let buf: &mut Vec<u8> = unsafe { &mut *(buf as *mut String).cast::<Vec<u8>>() };357 let bytes = value.as_bytes();358359 360 buf.reserve(value.len() + 2);361362 buf.push(b'"');363364 let mut start = 0;365366 for (i, &byte) in bytes.iter().enumerate() {367 let escape = ESCAPE[byte as usize];368 if escape == __ {369 continue;370 }371372 if start < i {373 buf.extend_from_slice(&bytes[start..i]);374 }375 start = i + 1;376377 match escape {378 self::BB | self::TT | self::NN | self::FF | self::RR | self::QU | self::BS => {379 buf.extend_from_slice(&[b'\\', escape]);380 }381 self::UU => {382 static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";383 let bytes = &[384 b'\\',385 b'u',386 b'0',387 b'0',388 HEX_DIGITS[(byte >> 4) as usize],389 HEX_DIGITS[(byte & 0xF) as usize],390 ];391 buf.extend_from_slice(bytes);392 }393 _ => unreachable!(),394 }395 }396397 if start == bytes.len() {398 buf.push(b'"');399 return;400 }401402 buf.extend_from_slice(&bytes[start..]);403 buf.push(b'"');404}