1extern crate alloc;
2
3use alloc::{string::String, vec::Vec};
4
5use facet_core::Facet;
6use facet_format::{FormatSerializer, ScalarValue, SerializeError, serialize_root};
7use facet_reflect::Peek;
8
9#[derive(Debug, Clone)]
11pub struct SerializeOptions {
12 pub pretty: bool,
14
15 pub indent: &'static str,
17
18 pub bytes_format: BytesFormat,
20}
21
22impl Default for SerializeOptions {
23 fn default() -> Self {
24 Self {
25 pretty: false,
26 indent: " ",
27 bytes_format: BytesFormat::default(),
28 }
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
34pub enum BytesFormat {
35 #[default]
37 Array,
38 Hex(HexBytesOptions),
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct HexBytesOptions {
45 pub truncate_above: Option<usize>,
49 pub head: usize,
51 pub tail: usize,
53}
54
55impl Default for HexBytesOptions {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl HexBytesOptions {
62 pub const fn new() -> Self {
64 Self {
65 truncate_above: None,
66 head: 32,
67 tail: 32,
68 }
69 }
70
71 pub const fn truncate(mut self, truncate_above: usize) -> Self {
73 self.truncate_above = Some(truncate_above);
74 self
75 }
76
77 pub const fn head_tail(mut self, head: usize, tail: usize) -> Self {
79 self.head = head;
80 self.tail = tail;
81 self
82 }
83}
84
85impl SerializeOptions {
86 pub fn new() -> Self {
88 Self::default()
89 }
90
91 pub const fn pretty(mut self) -> Self {
93 self.pretty = true;
94 self
95 }
96
97 pub const fn indent(mut self, indent: &'static str) -> Self {
99 self.indent = indent;
100 self.pretty = true;
101 self
102 }
103
104 pub const fn bytes_format(mut self, bytes_format: BytesFormat) -> Self {
106 self.bytes_format = bytes_format;
107 self
108 }
109
110 pub const fn bytes_as_array(mut self) -> Self {
112 self.bytes_format = BytesFormat::Array;
113 self
114 }
115
116 pub const fn bytes_as_hex(mut self) -> Self {
118 self.bytes_format = BytesFormat::Hex(HexBytesOptions::new());
119 self
120 }
121
122 pub const fn bytes_as_hex_with_options(mut self, options: HexBytesOptions) -> Self {
124 self.bytes_format = BytesFormat::Hex(options);
125 self
126 }
127}
128
129#[derive(Debug)]
130pub struct JsonSerializeError {
131 msg: &'static str,
132}
133
134impl core::fmt::Display for JsonSerializeError {
135 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136 f.write_str(self.msg)
137 }
138}
139
140impl std::error::Error for JsonSerializeError {}
141
142#[derive(Debug, Clone, Copy)]
143enum Ctx {
144 Struct { first: bool },
145 Seq { first: bool },
146}
147
148pub struct JsonSerializer {
150 out: Vec<u8>,
151 stack: Vec<Ctx>,
152 options: SerializeOptions,
153}
154
155impl JsonSerializer {
156 pub fn new() -> Self {
158 Self::with_options(SerializeOptions::default())
159 }
160
161 pub const fn with_options(options: SerializeOptions) -> Self {
163 Self {
164 out: Vec::new(),
165 stack: Vec::new(),
166 options,
167 }
168 }
169
170 pub fn finish(self) -> Vec<u8> {
172 self.out
173 }
174
175 const fn depth(&self) -> usize {
177 self.stack.len()
178 }
179
180 fn write_indent(&mut self) {
182 if self.options.pretty {
183 self.out.push(b'\n');
184 for _ in 0..self.depth() {
185 self.out.extend_from_slice(self.options.indent.as_bytes());
186 }
187 }
188 }
189
190 fn before_value(&mut self) -> Result<(), JsonSerializeError> {
191 match self.stack.last_mut() {
192 Some(Ctx::Seq { first }) => {
193 if !*first {
194 self.out.push(b',');
195 }
196 *first = false;
197 self.write_indent();
198 }
199 Some(Ctx::Struct { .. }) => {
200 }
202 None => {}
203 }
204 Ok(())
205 }
206
207 fn write_json_string(&mut self, s: &str) {
212 const STEP_SIZE: usize = 16; type Chunk = [u8; STEP_SIZE];
214
215 self.out.push(b'"');
216
217 let mut s = s;
218 while let Some(Ok(chunk)) = s.as_bytes().get(..STEP_SIZE).map(Chunk::try_from) {
219 let window = u128::from_ne_bytes(chunk);
220 let completely_ascii = window & 0x80808080808080808080808080808080 == 0;
226 let quote_free = !contains_byte(window, 0x22);
227 let backslash_free = !contains_byte(window, 0x5c);
228 let control_char_free = no_control_chars(window);
229
230 if completely_ascii && quote_free && backslash_free && control_char_free {
231 self.out.extend_from_slice(&chunk);
233 s = &s[STEP_SIZE..];
234 } else {
235 let mut chars = s.chars();
237 let mut count = STEP_SIZE;
238 for c in &mut chars {
239 self.write_json_escaped_char(c);
240 count = count.saturating_sub(c.len_utf8());
241 if count == 0 {
242 break;
243 }
244 }
245 s = chars.as_str();
246 }
247 }
248
249 for c in s.chars() {
251 self.write_json_escaped_char(c);
252 }
253
254 self.out.push(b'"');
255 }
256
257 #[inline]
258 fn write_json_escaped_char(&mut self, c: char) {
259 match c {
260 '"' => self.out.extend_from_slice(b"\\\""),
261 '\\' => self.out.extend_from_slice(b"\\\\"),
262 '\n' => self.out.extend_from_slice(b"\\n"),
263 '\r' => self.out.extend_from_slice(b"\\r"),
264 '\t' => self.out.extend_from_slice(b"\\t"),
265 '\u{08}' => self.out.extend_from_slice(b"\\b"),
266 '\u{0C}' => self.out.extend_from_slice(b"\\f"),
267 c if c.is_ascii_control() => {
268 let code_point = c as u32;
269 let to_hex = |d: u32| {
270 if d < 10 {
271 b'0' + d as u8
272 } else {
273 b'a' + (d - 10) as u8
274 }
275 };
276 let buf = [
277 b'\\',
278 b'u',
279 to_hex((code_point >> 12) & 0xF),
280 to_hex((code_point >> 8) & 0xF),
281 to_hex((code_point >> 4) & 0xF),
282 to_hex(code_point & 0xF),
283 ];
284 self.out.extend_from_slice(&buf);
285 }
286 c if c.is_ascii() => {
287 self.out.push(c as u8);
288 }
289 c => {
290 let mut buf = [0u8; 4];
291 let len = c.encode_utf8(&mut buf).len();
292 self.out.extend_from_slice(&buf[..len]);
293 }
294 }
295 }
296
297 #[inline]
298 fn write_hex_byte(&mut self, byte: u8) {
299 let hi = byte >> 4;
300 let lo = byte & 0x0f;
301 self.out
302 .push(if hi < 10 { b'0' + hi } else { b'a' + (hi - 10) });
303 self.out
304 .push(if lo < 10 { b'0' + lo } else { b'a' + (lo - 10) });
305 }
306
307 #[inline]
308 fn write_u8_decimal(&mut self, value: u8) {
309 #[cfg(feature = "fast")]
310 self.out
311 .extend_from_slice(itoa::Buffer::new().format(value).as_bytes());
312 #[cfg(not(feature = "fast"))]
313 self.out.extend_from_slice(value.to_string().as_bytes());
314 }
315
316 fn write_bytes_array(&mut self, bytes: &[u8]) {
317 self.out.push(b'[');
318 for (index, byte) in bytes.iter().copied().enumerate() {
319 if index != 0 {
320 self.out.push(b',');
321 }
322 self.write_u8_decimal(byte);
323 }
324 self.out.push(b']');
325 }
326
327 fn write_bytes_hex(&mut self, bytes: &[u8], options: HexBytesOptions) {
328 self.out.push(b'"');
329 self.out.extend_from_slice(b"0x");
330
331 let should_truncate = options
332 .truncate_above
333 .is_some_and(|max_len| bytes.len() > max_len);
334 if !should_truncate {
335 for byte in bytes {
336 self.write_hex_byte(*byte);
337 }
338 self.out.push(b'"');
339 return;
340 }
341
342 let head = options.head.min(bytes.len());
343 let max_tail = bytes.len().saturating_sub(head);
344 let tail = options.tail.min(max_tail);
345
346 for byte in &bytes[..head] {
347 self.write_hex_byte(*byte);
348 }
349
350 let omitted = bytes.len().saturating_sub(head + tail);
351 self.out.extend_from_slice(b"..<");
352 #[cfg(feature = "fast")]
353 self.out
354 .extend_from_slice(itoa::Buffer::new().format(omitted).as_bytes());
355 #[cfg(not(feature = "fast"))]
356 self.out.extend_from_slice(omitted.to_string().as_bytes());
357 self.out.extend_from_slice(b" bytes>..");
358
359 if tail != 0 {
360 for byte in &bytes[bytes.len() - tail..] {
361 self.write_hex_byte(*byte);
362 }
363 }
364
365 self.out.push(b'"');
366 }
367
368 fn write_bytes_with_options(&mut self, bytes: &[u8]) {
369 match self.options.bytes_format {
370 BytesFormat::Array => self.write_bytes_array(bytes),
371 BytesFormat::Hex(options) => self.write_bytes_hex(bytes, options),
372 }
373 }
374}
375
376#[inline]
379const fn contains_byte(val: u128, byte: u8) -> bool {
380 let mask = 0x01010101010101010101010101010101u128 * (byte as u128);
381 let xor_result = val ^ mask;
382 let has_zero = (xor_result.wrapping_sub(0x01010101010101010101010101010101))
383 & !xor_result
384 & 0x80808080808080808080808080808080;
385 has_zero != 0
386}
387
388#[inline]
391const fn no_control_chars(value: u128) -> bool {
392 let masked = value & 0xe0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0;
393 let has_zero = (masked.wrapping_sub(0x01010101010101010101010101010101))
394 & !masked
395 & 0x80808080808080808080808080808080;
396 has_zero == 0
397}
398
399impl Default for JsonSerializer {
400 fn default() -> Self {
401 Self::new()
402 }
403}
404
405impl FormatSerializer for JsonSerializer {
406 type Error = JsonSerializeError;
407
408 fn begin_struct(&mut self) -> Result<(), Self::Error> {
409 self.before_value()?;
410 self.out.push(b'{');
411 self.stack.push(Ctx::Struct { first: true });
412 Ok(())
413 }
414
415 fn field_key(&mut self, key: &str) -> Result<(), Self::Error> {
416 match self.stack.last_mut() {
417 Some(Ctx::Struct { first }) => {
418 if !*first {
419 self.out.push(b',');
420 }
421 *first = false;
422 self.write_indent();
423 self.write_json_string(key);
424 self.out.push(b':');
425 if self.options.pretty {
426 self.out.push(b' ');
427 }
428 Ok(())
429 }
430 _ => Err(JsonSerializeError {
431 msg: "field_key called outside of a struct",
432 }),
433 }
434 }
435
436 fn end_struct(&mut self) -> Result<(), Self::Error> {
437 match self.stack.pop() {
438 Some(Ctx::Struct { first }) => {
439 if !first {
441 self.write_indent();
442 }
443 self.out.push(b'}');
444 Ok(())
445 }
446 _ => Err(JsonSerializeError {
447 msg: "end_struct called without matching begin_struct",
448 }),
449 }
450 }
451
452 fn begin_seq(&mut self) -> Result<(), Self::Error> {
453 self.before_value()?;
454 self.out.push(b'[');
455 self.stack.push(Ctx::Seq { first: true });
456 Ok(())
457 }
458
459 fn end_seq(&mut self) -> Result<(), Self::Error> {
460 match self.stack.pop() {
461 Some(Ctx::Seq { first }) => {
462 if !first {
464 self.write_indent();
465 }
466 self.out.push(b']');
467 Ok(())
468 }
469 _ => Err(JsonSerializeError {
470 msg: "end_seq called without matching begin_seq",
471 }),
472 }
473 }
474
475 fn scalar(&mut self, scalar: ScalarValue<'_>) -> Result<(), Self::Error> {
476 self.before_value()?;
477 match scalar {
478 ScalarValue::Null | ScalarValue::Unit => self.out.extend_from_slice(b"null"),
479 ScalarValue::Bool(v) => {
480 if v {
481 self.out.extend_from_slice(b"true")
482 } else {
483 self.out.extend_from_slice(b"false")
484 }
485 }
486 ScalarValue::Char(c) => {
487 self.out.push(b'"');
488 self.write_json_escaped_char(c);
489 self.out.push(b'"');
490 }
491 ScalarValue::I64(v) => {
492 #[cfg(feature = "fast")]
493 self.out
494 .extend_from_slice(itoa::Buffer::new().format(v).as_bytes());
495 #[cfg(not(feature = "fast"))]
496 self.out.extend_from_slice(v.to_string().as_bytes());
497 }
498 ScalarValue::U64(v) => {
499 #[cfg(feature = "fast")]
500 self.out
501 .extend_from_slice(itoa::Buffer::new().format(v).as_bytes());
502 #[cfg(not(feature = "fast"))]
503 self.out.extend_from_slice(v.to_string().as_bytes());
504 }
505 ScalarValue::I128(v) => {
506 #[cfg(feature = "fast")]
507 self.out
508 .extend_from_slice(itoa::Buffer::new().format(v).as_bytes());
509 #[cfg(not(feature = "fast"))]
510 self.out.extend_from_slice(v.to_string().as_bytes());
511 }
512 ScalarValue::U128(v) => {
513 #[cfg(feature = "fast")]
514 self.out
515 .extend_from_slice(itoa::Buffer::new().format(v).as_bytes());
516 #[cfg(not(feature = "fast"))]
517 self.out.extend_from_slice(v.to_string().as_bytes());
518 }
519 ScalarValue::F64(v) => {
520 if v.is_nan() || v.is_infinite() {
521 self.out.extend_from_slice(b"null");
522 } else {
523 #[cfg(feature = "fast")]
524 self.out
525 .extend_from_slice(zmij::Buffer::new().format(v).as_bytes());
526 #[cfg(not(feature = "fast"))]
527 self.out.extend_from_slice(v.to_string().as_bytes());
528 }
529 }
530 ScalarValue::Str(s) => self.write_json_string(&s),
531 ScalarValue::Bytes(bytes) => self.write_bytes_with_options(bytes.as_ref()),
532 }
533 Ok(())
534 }
535
536 fn serialize_byte_sequence(&mut self, bytes: &[u8]) -> Result<bool, Self::Error> {
537 self.before_value()?;
538 self.write_bytes_with_options(bytes);
539 Ok(true)
540 }
541
542 fn serialize_byte_array(&mut self, bytes: &[u8]) -> Result<bool, Self::Error> {
543 self.serialize_byte_sequence(bytes)
544 }
545
546 fn raw_serialize_shape(&self) -> Option<&'static facet_core::Shape> {
547 Some(crate::RawJson::SHAPE)
548 }
549
550 fn raw_scalar(&mut self, content: &str) -> Result<(), Self::Error> {
551 self.before_value()?;
553 self.out.extend_from_slice(content.as_bytes());
554 Ok(())
555 }
556
557 fn format_namespace(&self) -> Option<&'static str> {
558 Some("json")
559 }
560}
561
562pub fn to_vec<'facet, T>(value: &'_ T) -> Result<Vec<u8>, SerializeError<JsonSerializeError>>
578where
579 T: Facet<'facet> + ?Sized,
580{
581 to_vec_with_options(value, &SerializeOptions::default())
582}
583
584pub fn to_vec_pretty<'facet, T>(value: &'_ T) -> Result<Vec<u8>, SerializeError<JsonSerializeError>>
600where
601 T: Facet<'facet> + ?Sized,
602{
603 to_vec_with_options(value, &SerializeOptions::default().pretty())
604}
605
606pub fn to_vec_with_options<'facet, T>(
628 value: &'_ T,
629 options: &SerializeOptions,
630) -> Result<Vec<u8>, SerializeError<JsonSerializeError>>
631where
632 T: Facet<'facet> + ?Sized,
633{
634 let mut serializer = JsonSerializer::with_options(options.clone());
635 serialize_root(&mut serializer, Peek::new(value))?;
636 Ok(serializer.finish())
637}
638
639pub fn to_string<'facet, T>(value: &'_ T) -> Result<String, SerializeError<JsonSerializeError>>
655where
656 T: Facet<'facet> + ?Sized,
657{
658 let bytes = to_vec(value)?;
659 Ok(String::from_utf8(bytes).expect("JSON output should always be valid UTF-8"))
661}
662
663pub fn to_string_pretty<'facet, T>(
679 value: &'_ T,
680) -> Result<String, SerializeError<JsonSerializeError>>
681where
682 T: Facet<'facet> + ?Sized,
683{
684 let bytes = to_vec_pretty(value)?;
685 Ok(String::from_utf8(bytes).expect("JSON output should always be valid UTF-8"))
686}
687
688pub fn to_string_with_options<'facet, T>(
710 value: &'_ T,
711 options: &SerializeOptions,
712) -> Result<String, SerializeError<JsonSerializeError>>
713where
714 T: Facet<'facet> + ?Sized,
715{
716 let bytes = to_vec_with_options(value, options)?;
717 Ok(String::from_utf8(bytes).expect("JSON output should always be valid UTF-8"))
718}
719
720pub fn peek_to_string<'input, 'facet>(
742 peek: Peek<'input, 'facet>,
743) -> Result<String, SerializeError<JsonSerializeError>> {
744 peek_to_string_with_options(peek, &SerializeOptions::default())
745}
746
747pub fn peek_to_string_pretty<'input, 'facet>(
764 peek: Peek<'input, 'facet>,
765) -> Result<String, SerializeError<JsonSerializeError>> {
766 peek_to_string_with_options(peek, &SerializeOptions::default().pretty())
767}
768
769pub fn peek_to_string_with_options<'input, 'facet>(
789 peek: Peek<'input, 'facet>,
790 options: &SerializeOptions,
791) -> Result<String, SerializeError<JsonSerializeError>> {
792 let mut serializer = JsonSerializer::with_options(options.clone());
793 serialize_root(&mut serializer, peek)?;
794 let bytes = serializer.finish();
795 Ok(String::from_utf8(bytes).expect("JSON output should always be valid UTF-8"))
796}
797
798pub fn to_writer_std<'facet, W, T>(writer: W, value: &T) -> std::io::Result<()>
820where
821 W: std::io::Write,
822 T: Facet<'facet> + ?Sized,
823{
824 peek_to_writer_std(writer, Peek::new(value))
825}
826
827pub fn to_writer_std_pretty<'facet, W, T>(writer: W, value: &T) -> std::io::Result<()>
847where
848 W: std::io::Write,
849 T: Facet<'facet> + ?Sized,
850{
851 peek_to_writer_std_pretty(writer, Peek::new(value))
852}
853
854pub fn to_writer_std_with_options<'facet, W, T>(
881 writer: W,
882 value: &T,
883 options: &SerializeOptions,
884) -> std::io::Result<()>
885where
886 W: std::io::Write,
887 T: Facet<'facet> + ?Sized,
888{
889 peek_to_writer_std_with_options(writer, Peek::new(value), options)
890}
891
892pub fn peek_to_writer_std<'input, 'facet, W>(
894 writer: W,
895 peek: Peek<'input, 'facet>,
896) -> std::io::Result<()>
897where
898 W: std::io::Write,
899{
900 peek_to_writer_std_with_options(writer, peek, &SerializeOptions::default())
901}
902
903pub fn peek_to_writer_std_pretty<'input, 'facet, W>(
905 writer: W,
906 peek: Peek<'input, 'facet>,
907) -> std::io::Result<()>
908where
909 W: std::io::Write,
910{
911 peek_to_writer_std_with_options(writer, peek, &SerializeOptions::default().pretty())
912}
913
914pub fn peek_to_writer_std_with_options<'input, 'facet, W>(
916 mut writer: W,
917 peek: Peek<'input, 'facet>,
918 options: &SerializeOptions,
919) -> std::io::Result<()>
920where
921 W: std::io::Write,
922{
923 let mut serializer = JsonSerializer::with_options(options.clone());
926 serialize_root(&mut serializer, peek)
927 .map_err(|e| std::io::Error::other(alloc::format!("{:?}", e)))?;
928 writer.write_all(&serializer.finish())
929}
930
931#[cfg(test)]
932mod tests {
933 use facet::Facet;
934
935 use super::{BytesFormat, HexBytesOptions, SerializeOptions, to_string_with_options};
936
937 #[derive(Facet)]
938 struct BytesVec {
939 data: Vec<u8>,
940 }
941
942 #[derive(Facet)]
943 struct BytesArray {
944 data: [u8; 4],
945 }
946
947 #[test]
948 fn bytes_default_to_json_array() {
949 let value = BytesVec {
950 data: vec![0, 127, 255],
951 };
952
953 let json = to_string_with_options(&value, &SerializeOptions::default()).unwrap();
954 assert_eq!(json, r#"{"data":[0,127,255]}"#);
955 }
956
957 #[test]
958 fn bytes_can_serialize_as_hex_string() {
959 let value = BytesVec {
960 data: vec![0x00, 0x7f, 0xff],
961 };
962
963 let json =
964 to_string_with_options(&value, &SerializeOptions::default().bytes_as_hex()).unwrap();
965 assert_eq!(json, r#"{"data":"0x007fff"}"#);
966 }
967
968 #[test]
969 fn bytes_can_serialize_as_truncated_hex_string() {
970 let value = BytesVec {
971 data: (0u8..10).collect(),
972 };
973
974 let options = SerializeOptions::default()
975 .bytes_as_hex_with_options(HexBytesOptions::new().truncate(6).head_tail(2, 2));
976 let json = to_string_with_options(&value, &options).unwrap();
977 assert_eq!(json, r#"{"data":"0x0001..<6 bytes>..0809"}"#);
978 }
979
980 #[test]
981 fn byte_arrays_use_same_hex_mode() {
982 let value = BytesArray {
983 data: [0xaa, 0xbb, 0xcc, 0xdd],
984 };
985
986 let options =
987 SerializeOptions::default().bytes_format(BytesFormat::Hex(HexBytesOptions::new()));
988 let json = to_string_with_options(&value, &options).unwrap();
989 assert_eq!(json, r#"{"data":"0xaabbccdd"}"#);
990 }
991}