contract_transcode/scon/
display.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17use super::{
18    Hex,
19    Map,
20    Seq,
21    Tuple,
22    Value,
23};
24use std::fmt::{
25    Debug,
26    Display,
27    Formatter,
28    LowerHex,
29    Result,
30};
31
32/// Wraps Value for custom Debug impl to provide pretty-printed Display
33struct DisplayValue<'a>(&'a Value);
34
35impl Debug for DisplayValue<'_> {
36    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
37        match &self.0 {
38            Value::Bool(boolean) => <bool as Debug>::fmt(boolean, f),
39            Value::Char(character) => <char as Debug>::fmt(character, f),
40            Value::UInt(uint) => <u128 as Display>::fmt(uint, f),
41            Value::Int(integer) => <i128 as Display>::fmt(integer, f),
42            Value::Map(map) => <DisplayMap as Debug>::fmt(&DisplayMap(map), f),
43            Value::Tuple(tuple) => <DisplayTuple as Debug>::fmt(&DisplayTuple(tuple), f),
44            Value::String(string) => <String as Display>::fmt(string, f),
45            Value::Seq(seq) => <DisplaySeq as Debug>::fmt(&DisplaySeq(seq), f),
46            Value::Hex(hex) => <Hex as Debug>::fmt(hex, f),
47            Value::Literal(literal) => <String as Display>::fmt(literal, f),
48            Value::Unit => write!(f, "()"),
49        }
50    }
51}
52
53impl Display for Value {
54    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
55        match self {
56            Value::String(string) => <String as Display>::fmt(string, f),
57            value => <DisplayValue as Debug>::fmt(&DisplayValue(value), f),
58        }
59    }
60}
61
62struct DisplayMap<'a>(&'a Map);
63
64impl Debug for DisplayMap<'_> {
65    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
66        match self.0.ident {
67            Some(ref name) => {
68                let mut builder = f.debug_struct(name);
69                for (name, value) in self.0.map.iter() {
70                    builder.field(&format!("{name}"), &DisplayValue(value));
71                }
72                builder.finish()
73            }
74            None => {
75                let mut builder = f.debug_map();
76                for (name, value) in self.0.map.iter() {
77                    builder.entry(name, &DisplayValue(value));
78                }
79                builder.finish()
80            }
81        }
82    }
83}
84
85struct DisplayTuple<'a>(&'a Tuple);
86
87impl Debug for DisplayTuple<'_> {
88    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
89        let name = self.0.ident.as_ref().map_or("", |s| s.as_str());
90        let mut builder = f.debug_tuple(name);
91        for value in self.0.values.iter() {
92            builder.field(&DisplayValue(value));
93        }
94        builder.finish()
95    }
96}
97
98struct DisplaySeq<'a>(&'a Seq);
99
100impl Debug for DisplaySeq<'_> {
101    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
102        let mut builder = f.debug_list();
103        for elem in &self.0.elems {
104            builder.entry(&DisplayValue(elem));
105        }
106        builder.finish()
107    }
108}
109
110impl Debug for Hex {
111    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
112        write!(f, "{self:#x}")
113    }
114}
115
116impl LowerHex for Hex {
117    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
118        if f.alternate() {
119            write!(f, "0x{}", hex::encode(&self.bytes))
120        } else {
121            write!(f, "{}", hex::encode(&self.bytes))
122        }
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn display_map() {
132        let map = Value::Map(Map::new(
133            Some("M"),
134            vec![(Value::String("a".into()), Value::UInt(1))]
135                .into_iter()
136                .collect(),
137        ));
138        assert_eq!(r#"M { a: 1 }"#, format!("{map}"), "non-alternate same line");
139        assert_eq!(
140            "M {\n    a: 1,\n}",
141            format!("{map:#}"),
142            "alternate indented (pretty)"
143        );
144    }
145}