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("\n\n");181 buf.push_str(cur_padding);182 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {183 buf.push(' ');184 }185 buf.push(']');186 }187 Val::Obj(obj) => {188 obj.run_assertions()?;189 buf.push('{');190 let fields = obj.fields(191 #[cfg(feature = "exp-preserve-order")]192 options.preserve_order,193 );194 if !fields.is_empty() {195 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {196 buf.push_str(options.newline);197 }198199 let old_len = cur_padding.len();200 cur_padding.push_str(&options.padding);201 for (i, field) in fields.into_iter().enumerate() {202 if i != 0 {203 buf.push(',');204 if mtype == JsonFormatting::ToString {205 buf.push(' ');206 } else if mtype != JsonFormatting::Minify {207 buf.push_str(options.newline);208 }209 }210 buf.push_str(cur_padding);211 escape_string_json_buf(&field, buf);212 buf.push_str(options.key_val_sep);213 State::push_description(214 || format!("field <{}> manifestification", field.clone()),215 || {216 let value = obj.get(field.clone())?.unwrap();217 manifest_json_ex_buf(&value, buf, cur_padding, options)?;218 Ok(())219 },220 )?;221 }222 cur_padding.truncate(old_len);223224 if mtype != JsonFormatting::ToString && mtype != JsonFormatting::Minify {225 buf.push_str(options.newline);226 buf.push_str(cur_padding);227 }228 } else if mtype == JsonFormatting::Std {229 buf.push_str("\n\n");230 buf.push_str(cur_padding);231 } else if mtype == JsonFormatting::ToString || mtype == JsonFormatting::Manifest {232 buf.push(' ');233 }234 buf.push('}');235 }236 Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),237 };238 Ok(())239}240241impl ManifestFormat for JsonFormat<'_> {242 fn manifest_buf(&self, val: Val, buf: &mut String) -> Result<()> {243 manifest_json_ex_buf(&val, buf, &mut String::new(), self)244 }245}246247pub struct ToStringFormat;248impl ManifestFormat for ToStringFormat {249 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {250 JsonFormat::std_to_string().manifest_buf(val, out)251 }252}253pub struct StringFormat;254impl ManifestFormat for StringFormat {255 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {256 let Val::Str(s) = val else {257 throw!("output should be string for string manifest format, got {}", val.value_type())258 };259 write!(out, "{s}").unwrap();260 Ok(())261 }262}263264pub struct YamlStreamFormat<I>(pub I);265impl<I: ManifestFormat> ManifestFormat for YamlStreamFormat<I> {266 fn manifest_buf(&self, val: Val, out: &mut String) -> Result<()> {267 let Val::Arr(arr) = val else {268 throw!("output should be array for yaml stream format, got {}", val.value_type())269 };270 if !arr.is_empty() {271 for v in arr.iter() {272 let v = v?;273 out.push_str("---\n");274 self.0.manifest_buf(v, out)?;275 out.push('\n');276 }277 out.push_str("...");278 }279 Ok(())280 }281}282283pub fn escape_string_json(s: &str) -> String {284 let mut buf = String::new();285 escape_string_json_buf(s, &mut buf);286 buf287}288289290291const BB: u8 = b'b'; 292const TT: u8 = b't'; 293const NN: u8 = b'n'; 294const FF: u8 = b'f'; 295const RR: u8 = b'r'; 296const QU: u8 = b'"'; 297const BS: u8 = b'\\'; 298const UU: u8 = b'u'; 299const __: u8 = 0;300301302303static ESCAPE: [u8; 256] = [304 305 UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, 306 UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, 307 __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, 308 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 309 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 310 __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, 311 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 312 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 313 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 314 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 315 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 316 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 317 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 318 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 319 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 320 __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, 321];322323pub fn escape_string_json_buf(value: &str, buf: &mut String) {324 325 let buf: &mut Vec<u8> = unsafe { &mut *(buf as *mut String).cast::<Vec<u8>>() };326 let bytes = value.as_bytes();327328 329 buf.reserve(value.len() + 2);330331 buf.push(b'"');332333 let mut start = 0;334335 for (i, &byte) in bytes.iter().enumerate() {336 let escape = ESCAPE[byte as usize];337 if escape == __ {338 continue;339 }340341 if start < i {342 buf.extend_from_slice(&bytes[start..i]);343 }344 start = i + 1;345346 match escape {347 self::BB | self::TT | self::NN | self::FF | self::RR | self::QU | self::BS => {348 buf.extend_from_slice(&[b'\\', escape]);349 }350 self::UU => {351 static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";352 let bytes = &[353 b'\\',354 b'u',355 b'0',356 b'0',357 HEX_DIGITS[(byte >> 4) as usize],358 HEX_DIGITS[(byte & 0xF) as usize],359 ];360 buf.extend_from_slice(bytes);361 }362 _ => unreachable!(),363 }364 }365366 if start == bytes.len() {367 buf.push(b'"');368 return;369 }370371 buf.extend_from_slice(&bytes[start..]);372 buf.push(b'"');373}