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 Val::Arr(items) => {153 buf.push('[');154 if !items.is_empty() {155 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {156 buf.push_str(options.newline);157 }158159 let old_len = cur_padding.len();160 cur_padding.push_str(&options.padding);161 for (i, item) in items.iter().enumerate() {162 if i != 0 {163 buf.push(',');164 if mtype == JsonFormatting::ToString {165 buf.push(' ');166 } else if mtype != JsonFormatting::Minify {167 buf.push_str(options.newline);168 }169 }170 buf.push_str(cur_padding);171 manifest_json_ex_buf(&item?, buf, cur_padding, options)?;172 }173 cur_padding.truncate(old_len);174175 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {176 buf.push_str(options.newline);177 buf.push_str(cur_padding);178 }179 } else if mtype == JsonFormatting::Std {180 buf.push_str(options.newline);181 buf.push_str(options.newline);182 buf.push_str(cur_padding);183 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {184 buf.push(' ');185 }186 buf.push(']');187 }188 Val::Obj(obj) => {189 obj.run_assertions()?;190 buf.push('{');191 let fields = obj.fields(192 #[cfg(feature = "exp-preserve-order")]193 options.preserve_order,194 );195 if !fields.is_empty() {196 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {197 buf.push_str(options.newline);198 }199200 let old_len = cur_padding.len();201 cur_padding.push_str(&options.padding);202 for (i, field) in fields.into_iter().enumerate() {203 if i != 0 {204 buf.push(',');205 if mtype == JsonFormatting::ToString {206 buf.push(' ');207 } else if mtype != JsonFormatting::Minify {208 buf.push_str(options.newline);209 }210 }211 buf.push_str(cur_padding);212 escape_string_json_buf(&field, buf);213 buf.push_str(options.key_val_sep);214 State::push_description(215 || format!("field <{}> manifestification", field.clone()),216 || {217 let value = obj.get(field.clone())?.unwrap();218 manifest_json_ex_buf(&value, buf, cur_padding, options)?;219 Ok(())220 },221 )?;222 }223 cur_padding.truncate(old_len);224225 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {226 buf.push_str(options.newline);227 buf.push_str(cur_padding);228 }229 } else if mtype == JsonFormatting::Std {230 buf.push_str(options.newline);231 buf.push_str(options.newline);232 buf.push_str(cur_padding);233 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {234 buf.push(' ');235 }236 buf.push('}');237 }238 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),239 };240 Ok(())241}242243impl ManifestFormat for JsonFormat<'_> {244 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {245 manifest_json_ex_buf(&val, buf, &mut String::new(), self)246 }247}248249pub struct ToStringFormat;250impl ManifestFormat for ToStringFormat {251 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {252 JsonFormat::std_to_string().manifest_buf(val, out)253 }254}255pub struct StringFormat;256impl ManifestFormat for StringFormat {257 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {258 let Val::Str(s) = val else {259 throw!("output should be string for string manifest format, got {}", val.value_type())260 };261 write!(out, "{s}").unwrap();262 Ok(())263 }264}265266pub struct YamlStreamFormat<I>(pub I);267impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {268 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {269 let Val::Arr(arr) = val else {270 throw!("output should be array for yaml stream format, got {}", val.value_type())271 };272 if !arr.is_empty() {273 for v in arr.iter() {274 let v = v?;275 out.push_str("---\n");276 self.0.manifest_buf(v, out)?;277 out.push('\n');278 }279 out.push_str("...");280 }281 Ok(())282 }283}284285pub fn escape_string_json(s: &str) -> String {286 let mut buf = String::new();287 escape_string_json_buf(s, &mut buf);288 buf289}290291292293const BB: u8 = b'b'; 294const TT: u8 = b't'; 295const NN: u8 = b'n'; 296const FF: u8 = b'f'; 297const RR: u8 = b'r'; 298const QU: u8 = b'"'; 299const BS: u8 = b'\\'; 300const UU: u8 = b'u'; 301const __: u8 = 0;302303304305static ESCAPE: [u8; 256] = [306 307 UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, 308 UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, 309 __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, 310 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 311 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 312 __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, 313 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 314 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 315 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 316 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 317 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 318 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 319 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 320 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 321 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 322 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 323];324325pub fn escape_string_json_buf(value: &str, buf: &mut String) {326 327 let buf: &mut Vec<u8> = unsafe { &mut *(buf as *mut String).cast::<Vec<u8>>() };328 let bytes = value.as_bytes();329330 331 buf.reserve(value.len() + 2);332333 buf.push(b'"');334335 let mut start = 0;336337 for (i, &byte) in bytes.iter().enumerate() {338 let escape = ESCAPE[byte as usize];339 if escape == __ {340 continue;341 }342343 if start < i {344 buf.extend_from_slice(&bytes[start..i]);345 }346 start = i + 1;347348 match escape {349 self::BB | self::TT | self::NN | self::FF | self::RR | self::QU | self::BS => {350 buf.extend_from_slice(&[b'\\', escape]);351 }352 self::UU => {353 static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";354 let bytes = &[355 b'\\',356 b'u',357 b'0',358 b'0',359 HEX_DIGITS[(byte >> 4) as usize],360 HEX_DIGITS[(byte & 0xF) as usize],361 ];362 buf.extend_from_slice(bytes);363 }364 _ => unreachable!(),365 }366 }367368 if start == bytes.len() {369 buf.push(b'"');370 return;371 }372373 buf.extend_from_slice(&bytes[start..]);374 buf.push(b'"');375}