1use super::string_helpers;
17use crate::prelude::*;
18use crate::value_type::{BitSequence, Composite, Primitive, Value, ValueDef, Variant};
19use core::fmt::{Display, Write};
20
21pub struct ToWriterBuilder<T, W> {
24 style: FormatStyle,
25 custom_formatters: Vec<CustomFormatter<T, W>>,
26 indent_by: String,
27 leading_indent: String,
28 print_context: Option<ContextPrinter<T, W>>,
29}
30
31type CustomFormatter<T, W> = Box<dyn Fn(&Value<T>, &mut W) -> Option<core::fmt::Result> + 'static>;
32type ContextPrinter<T, W> = Box<dyn Fn(&T, &mut W) -> core::fmt::Result + 'static>;
33
34impl<T, W: core::fmt::Write> ToWriterBuilder<T, W> {
35 pub(crate) fn new() -> Self {
36 ToWriterBuilder {
37 style: FormatStyle::Normal,
38 custom_formatters: Vec::new(),
39 indent_by: " ".to_owned(),
40 leading_indent: String::new(),
41 print_context: None,
42 }
43 }
44
45 pub fn compact(mut self) -> Self {
68 self.style = FormatStyle::Compact;
69 self
70 }
71
72 pub fn pretty(mut self) -> Self {
104 self.style = FormatStyle::Pretty(0);
105 self
106 }
107
108 pub fn leading_indent(mut self, s: impl Into<String>) -> Self {
143 self.leading_indent = s.into();
144 self
145 }
146
147 pub fn indent_by(mut self, s: impl Into<String>) -> Self {
182 self.indent_by = s.into();
183 self
184 }
185
186 pub fn format_context<F: Fn(&T, &mut W) -> core::fmt::Result + 'static>(
217 mut self,
218 f: F,
219 ) -> Self {
220 self.print_context = Some(Box::new(f));
221 self
222 }
223
224 pub fn add_custom_formatter<F: Fn(&Value<T>, &mut W) -> Option<core::fmt::Result> + 'static>(
236 mut self,
237 f: F,
238 ) -> Self {
239 self.custom_formatters.push(Box::new(f));
240 self
241 }
242
243 pub fn write(&self, value: &Value<T>, writer: W) -> core::fmt::Result {
245 let mut formatter = self.as_formatter(writer);
246 fmt_value(value, &mut formatter)
247 }
248
249 fn as_formatter(&self, writer: W) -> Formatter<'_, T, W> {
250 Formatter {
251 writer,
252 style: self.style,
253 custom_formatters: &self.custom_formatters,
254 indent_by: &self.indent_by,
255 leading_indent: &self.leading_indent,
256 print_context: &self.print_context,
257 }
258 }
259}
260
261struct Formatter<'a, T, W> {
262 writer: W,
263 style: FormatStyle,
264 custom_formatters: &'a [CustomFormatter<T, W>],
265 indent_by: &'a str,
266 leading_indent: &'a str,
267 print_context: &'a Option<ContextPrinter<T, W>>,
268}
269
270impl<'a, T, W: core::fmt::Write> Formatter<'a, T, W> {
271 fn indent_step(&mut self) {
272 self.style = match &self.style {
273 FormatStyle::Compact => FormatStyle::Compact,
274 FormatStyle::Normal => FormatStyle::Normal,
275 FormatStyle::Pretty(n) => FormatStyle::Pretty(n + 1),
276 };
277 }
278 fn unindent_step(&mut self) {
279 self.style = match &self.style {
280 FormatStyle::Compact => FormatStyle::Compact,
281 FormatStyle::Normal => FormatStyle::Normal,
282 FormatStyle::Pretty(n) => FormatStyle::Pretty(n.saturating_sub(1)),
283 };
284 }
285 fn space(&mut self) -> core::fmt::Result {
286 match self.style {
287 FormatStyle::Compact => Ok(()),
288 FormatStyle::Normal | FormatStyle::Pretty(_) => self.writer.write_char(' '),
289 }
290 }
291 fn newline(&mut self) -> core::fmt::Result {
292 match self.style {
293 FormatStyle::Compact | FormatStyle::Normal => Ok(()),
294 FormatStyle::Pretty(n) => {
295 write_newline(&mut self.writer, self.leading_indent, self.indent_by, n)
296 }
297 }
298 }
299 fn item_separator(&mut self) -> core::fmt::Result {
300 match self.style {
301 FormatStyle::Compact => Ok(()),
302 FormatStyle::Normal => self.writer.write_char(' '),
303 FormatStyle::Pretty(n) => {
304 write_newline(&mut self.writer, self.leading_indent, self.indent_by, n)
305 }
306 }
307 }
308 fn should_print_context(&self) -> bool {
309 self.print_context.is_some()
310 }
311 fn print_context(&mut self, ctx: &T) -> core::fmt::Result {
312 if let Some(f) = &self.print_context {
313 f(ctx, &mut self.writer)
314 } else {
315 Ok(())
316 }
317 }
318 fn print_custom_format(&mut self, value: &Value<T>) -> Option<core::fmt::Result> {
319 for formatter in self.custom_formatters {
320 if let Some(res) = formatter(value, &mut self.writer) {
322 return Some(res);
323 }
324 }
325 None
326 }
327}
328
329impl<'a, T, W: core::fmt::Write> core::fmt::Write for Formatter<'a, T, W> {
330 fn write_str(&mut self, s: &str) -> core::fmt::Result {
331 self.writer.write_str(s)
334 }
335}
336
337fn write_newline(
338 writer: &mut impl core::fmt::Write,
339 leading_indent: &str,
340 indent_str: &str,
341 indent: usize,
342) -> core::fmt::Result {
343 writer.write_char('\n')?;
344 writer.write_str(leading_indent)?;
345 for _ in 0..indent {
346 writer.write_str(indent_str)?;
347 }
348 Ok(())
349}
350
351#[derive(Clone, Copy)]
355enum FormatStyle {
356 Pretty(usize),
358 Normal,
360 Compact,
362}
363
364fn default_builder<T, W: core::fmt::Write>(alternate: bool) -> ToWriterBuilder<T, W> {
366 let mut builder = ToWriterBuilder::new();
367 if alternate {
368 builder = builder.pretty();
369 }
370 builder
371}
372
373impl<T> Display for Value<T> {
374 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
375 self.value.fmt(f)
376 }
377}
378
379impl<T> Display for ValueDef<T> {
380 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
381 let builder = default_builder(f.alternate());
382 fmt_valuedef(self, &mut builder.as_formatter(f))
383 }
384}
385
386impl<T> Display for Composite<T> {
387 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
388 let builder = default_builder(f.alternate());
389 fmt_composite(self, &mut builder.as_formatter(f))
390 }
391}
392
393impl<T> Display for Variant<T> {
394 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
395 let builder = default_builder(f.alternate());
396 fmt_variant(self, &mut builder.as_formatter(f))
397 }
398}
399
400impl Display for Primitive {
401 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
402 let builder = default_builder::<(), _>(f.alternate());
403 fmt_primitive(self, &mut builder.as_formatter(f))
404 }
405}
406
407fn fmt_value<T, W: core::fmt::Write>(v: &Value<T>, f: &mut Formatter<T, W>) -> core::fmt::Result {
408 if f.should_print_context() {
409 f.write_char('<')?;
410 f.print_context(&v.context)?;
411 f.write_str("> ")?;
412 }
413
414 f.print_custom_format(v).unwrap_or_else(|| fmt_valuedef(&v.value, f))
416}
417
418fn fmt_valuedef<T, W: core::fmt::Write>(
419 v: &ValueDef<T>,
420 f: &mut Formatter<T, W>,
421) -> core::fmt::Result {
422 match v {
423 ValueDef::Composite(c) => fmt_composite(c, f),
424 ValueDef::Variant(v) => fmt_variant(v, f),
425 ValueDef::BitSequence(b) => fmt_bitsequence(b, f),
426 ValueDef::Primitive(p) => fmt_primitive(p, f),
427 }
428}
429
430fn fmt_variant<T, W: core::fmt::Write>(
431 v: &Variant<T>,
432 f: &mut Formatter<T, W>,
433) -> core::fmt::Result {
434 if is_ident(&v.name) {
435 f.write_str(&v.name)?;
436 } else {
437 f.write_char('v')?;
442 fmt_string(&v.name, f)?;
443 }
444 f.space()?;
445 fmt_composite(&v.values, f)
446}
447
448fn fmt_composite<T, W: core::fmt::Write>(
449 v: &Composite<T>,
450 f: &mut Formatter<T, W>,
451) -> core::fmt::Result {
452 match v {
453 Composite::Named(vals) => {
454 if vals.is_empty() {
455 f.write_str("{}")?;
456 } else {
457 f.write_str("{")?;
458 f.indent_step();
459 f.item_separator()?;
460 for (idx, (name, val)) in vals.iter().enumerate() {
461 if idx != 0 {
462 f.write_str(",")?;
463 f.item_separator()?;
464 }
465 if is_ident(name) {
466 f.write_str(name)?;
467 } else {
468 fmt_string(name, f)?;
469 }
470 f.write_char(':')?;
471 f.space()?;
472 fmt_value(val, f)?;
473 }
474 f.unindent_step();
475 f.item_separator()?;
476 f.write_str("}")?;
477 }
478 }
479 Composite::Unnamed(vals) => {
480 if vals.is_empty() {
481 f.write_str("()")?;
482 } else {
483 f.write_char('(')?;
484 f.indent_step();
485 f.newline()?;
486 for (idx, val) in vals.iter().enumerate() {
487 if idx != 0 {
488 f.write_str(",")?;
489 f.item_separator()?;
490 }
491 fmt_value(val, f)?;
492 }
493 f.unindent_step();
494 f.newline()?;
495 f.write_char(')')?;
496 }
497 }
498 }
499 Ok(())
500}
501
502fn fmt_primitive<T, W: core::fmt::Write>(
503 p: &Primitive,
504 f: &mut Formatter<T, W>,
505) -> core::fmt::Result {
506 match p {
507 Primitive::Bool(true) => f.write_str("true"),
508 Primitive::Bool(false) => f.write_str("false"),
509 Primitive::Char(c) => fmt_char(*c, f),
510 Primitive::I128(n) => write!(f, "{n}"),
511 Primitive::U128(n) => write!(f, "{n}"),
512 Primitive::String(s) => fmt_string(s, f),
513 Primitive::U256(_) | Primitive::I256(_) => Err(core::fmt::Error),
516 }
517}
518
519fn fmt_string<T, W: core::fmt::Write>(s: &str, f: &mut Formatter<T, W>) -> core::fmt::Result {
520 f.write_char('"')?;
521 for char in s.chars() {
522 match string_helpers::to_escape_code(char) {
523 Some(escaped) => {
524 f.write_char('\\')?;
525 f.write_char(escaped)?
526 }
527 None => f.write_char(char)?,
528 }
529 }
530 f.write_char('"')
531}
532
533fn fmt_char<T, W: core::fmt::Write>(c: char, f: &mut Formatter<T, W>) -> core::fmt::Result {
534 f.write_char('\'')?;
535 match string_helpers::to_escape_code(c) {
536 Some(escaped) => {
537 f.write_char('\\')?;
538 f.write_char(escaped)?
539 }
540 None => f.write_char(c)?,
541 }
542 f.write_char('\'')
543}
544
545fn fmt_bitsequence<T, W: core::fmt::Write>(
546 b: &BitSequence,
547 f: &mut Formatter<T, W>,
548) -> core::fmt::Result {
549 f.write_char('<')?;
550 for bit in b.iter() {
551 match bit {
552 true => f.write_char('1')?,
553 false => f.write_char('0')?,
554 }
555 }
556 f.write_char('>')
557}
558
559fn is_ident(s: &str) -> bool {
561 let mut chars = s.chars();
562
563 let Some(fst) = chars.next() else { return false };
565 if !fst.is_alphabetic() {
566 return false;
567 }
568
569 for c in chars {
571 if !c.is_alphanumeric() && c != '_' {
572 return false;
573 }
574 }
575 true
576}
577
578#[cfg(test)]
579mod test {
580 use crate::value;
581
582 use super::*;
583
584 #[test]
585 fn outputs_expected_string() {
586 let expected = [
587 (Value::bool(true), "true"),
588 (Value::bool(false), "false"),
589 (Value::char('a'), "'a'"),
590 (Value::u128(123), "123"),
591 (value!((true, "hi")), r#"(true, "hi")"#),
592 (
593 Value::named_composite([
594 ("hi there", Value::bool(true)),
595 ("other", Value::string("hi")),
596 ]),
597 r#"{ "hi there": true, other: "hi" }"#,
598 ),
599 (value!(Foo { ns: (1u8, 2u8, 3u8), other: 'a' }), "Foo { ns: (1, 2, 3), other: 'a' }"),
600 ];
601
602 for (value, expected_str) in expected {
603 assert_eq!(&value.to_string(), expected_str);
604 }
605 }
606
607 #[test]
608 fn expanded_output_works() {
609 let v = value!({
610 hello: true,
611 empty: (),
612 sequence: (1,2,3),
613 variant: MyVariant (1,2,3),
614 inner: {
615 foo: "hello"
616 }
617 });
618
619 assert_eq!(
620 format!("{v:#}"),
621 "{
622 hello: true,
623 empty: (),
624 sequence: (
625 1,
626 2,
627 3
628 ),
629 variant: MyVariant (
630 1,
631 2,
632 3
633 ),
634 inner: {
635 foo: \"hello\"
636 }
637}"
638 );
639 }
640
641 #[test]
642 fn compact_output_works() {
643 let v = value!({
644 hello: true,
645 empty: (),
646 sequence: (1u8,2u8,3u8),
647 variant: MyVariant (1u8,2u8,3u8),
648 inner: {
649 foo: "hello"
650 }
651 });
652
653 let mut s = String::new();
654 ToWriterBuilder::new().compact().write(&v, &mut s).unwrap();
655
656 assert_eq!(
657 s,
658 "{hello:true,empty:(),sequence:(1,2,3),variant:MyVariant(1,2,3),inner:{foo:\"hello\"}}"
659 );
660 }
661
662 #[test]
663 fn pretty_variant_ident_used_when_possible() {
664 let expected = [
665 ("simpleIdent", true),
666 ("S", true),
667 ("S123", true),
668 ("S123_", true),
669 ("", false),
670 ("complex ident", false),
671 ("0Something", false),
672 ("_Something", false),
673 ];
674
675 for (ident, should_be_simple) in expected {
676 let v = Value::variant(ident, Composite::Named(vec![]));
677 let s = v.to_string();
678 assert_eq!(
679 !s.trim().starts_with('v'),
680 should_be_simple,
681 "{s} should be simple: {should_be_simple}"
682 );
683 }
684 }
685
686 #[cfg(feature = "from-string")]
688 mod from_to {
689 use super::*;
690
691 fn assert_from_to<T: core::fmt::Debug + PartialEq>(val: Value<T>) {
692 let s = val.to_string();
693 match crate::stringify::from_str(&s) {
694 (Err(e), _) => {
695 panic!("'{s}' cannot be parsed back into the value {val:?}: {e}");
696 }
697 (Ok(new_val), rest) => {
698 assert_eq!(
699 val.remove_context(),
700 new_val,
701 "value should be the same after parsing to/from a string"
702 );
703 assert_eq!(
704 rest.len(),
705 0,
706 "there should be no unparsed string but got '{rest}'"
707 );
708 }
709 }
710 }
711
712 #[test]
713 fn primitives() {
714 assert_from_to(Value::bool(true));
715 assert_from_to(Value::bool(false));
716
717 assert_from_to(Value::char('\n'));
718 assert_from_to(Value::char('😀'));
719 assert_from_to(Value::char('a'));
720 assert_from_to(Value::char('\0'));
721 assert_from_to(Value::char('\t'));
722
723 assert_from_to(Value::i128(-123_456));
724 assert_from_to(Value::u128(0));
725 assert_from_to(Value::u128(123456));
726
727 assert_from_to(Value::string("hello \"you\",\n\n\t How are you??"));
728 assert_from_to(Value::string(""));
729 }
730
731 #[test]
732 fn composites() {
733 assert_from_to(Value::named_composite([
734 ("foo", Value::u128(12345)),
735 ("bar", Value::bool(true)),
736 ("a \"weird\" name", Value::string("Woop!")),
737 ]));
738 assert_from_to(Value::unnamed_composite([
739 Value::u128(12345),
740 Value::bool(true),
741 Value::string("Woop!"),
742 ]));
743 }
744
745 #[test]
746 fn variants() {
747 assert_from_to(Value::named_variant(
748 "A weird variant name",
749 [
750 ("foo", Value::u128(12345)),
751 ("bar", Value::bool(true)),
752 ("a \"weird\" name", Value::string("Woop!")),
753 ],
754 ));
755 assert_from_to(value!(MyVariant(12345u32, true, "Woop!")));
756 }
757
758 #[test]
759 fn bit_sequences() {
760 use scale_bits::bits;
761 assert_from_to(Value::bit_sequence(bits![0, 1, 1, 0, 1, 1, 0]));
762 assert_from_to(Value::bit_sequence(bits![]));
763 }
764 }
765}