Skip to main content

capnp/
stringify.rs

1use crate::dynamic_value;
2use core::fmt::{self, Formatter};
3
4#[derive(Clone, Copy, Debug, Default)]
5pub(crate) struct Indent {
6    /// None means everything gets printed on a single line.
7    indent: Option<usize>,
8}
9
10impl Indent {
11    fn no_indent() -> Self {
12        Self { indent: None }
13    }
14
15    fn enabled() -> Self {
16        Self { indent: Some(0) }
17    }
18
19    fn next(self) -> Self {
20        match self.indent {
21            None => self,
22            Some(x) => Self {
23                indent: Some(x + 1),
24            },
25        }
26    }
27
28    fn maybe_newline(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
29        match self.indent {
30            None => Ok(()),
31            Some(indent) => {
32                formatter.write_str("\n")?;
33                for _ in 0..indent {
34                    formatter.write_str("  ")?;
35                }
36                Ok(())
37            }
38        }
39    }
40
41    fn comma(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
42        match self.indent {
43            None => formatter.write_str(", "),
44            Some(_) => formatter.write_str(","),
45        }
46    }
47}
48
49fn cvt<T, E>(r: core::result::Result<T, E>) -> Result<T, fmt::Error> {
50    r.map_err(|_| fmt::Error)
51}
52
53pub(crate) fn print(
54    value: dynamic_value::Reader,
55    formatter: &mut Formatter,
56    indent: Indent,
57) -> Result<(), fmt::Error> {
58    match value {
59        dynamic_value::Reader::Void => formatter.write_str("()"),
60        dynamic_value::Reader::Bool(b) => write!(formatter, "{b}"),
61        dynamic_value::Reader::Int8(x) => write!(formatter, "{x}"),
62        dynamic_value::Reader::Int16(x) => write!(formatter, "{x}"),
63        dynamic_value::Reader::Int32(x) => write!(formatter, "{x}"),
64        dynamic_value::Reader::Int64(x) => write!(formatter, "{x}"),
65        dynamic_value::Reader::UInt8(x) => write!(formatter, "{x}"),
66        dynamic_value::Reader::UInt16(x) => write!(formatter, "{x}"),
67        dynamic_value::Reader::UInt32(x) => write!(formatter, "{x}"),
68        dynamic_value::Reader::UInt64(x) => write!(formatter, "{x}"),
69        dynamic_value::Reader::Float32(x) => write!(formatter, "{x}"),
70        dynamic_value::Reader::Float64(x) => write!(formatter, "{x}"),
71        dynamic_value::Reader::Enum(e) => match cvt(e.get_enumerant())? {
72            Some(enumerant) => {
73                formatter.write_str(cvt(cvt(enumerant.get_proto().get_name())?.to_str())?)
74            }
75            None => write!(formatter, "{}", e.get_value()),
76        },
77        dynamic_value::Reader::Text(t) => write!(formatter, "{t:?}"),
78        dynamic_value::Reader::Data(d) => {
79            formatter.write_str("0x\"")?;
80            for b in d {
81                write!(formatter, "{:02x}", *b)?;
82            }
83            formatter.write_str("\"")
84        }
85        dynamic_value::Reader::List(list) => {
86            if list.is_empty() {
87                formatter.write_str("[]")
88            } else {
89                formatter.write_str("[")?;
90                let indent2 = indent.next();
91                for (idx, value) in list.iter().enumerate() {
92                    indent2.maybe_newline(formatter)?;
93                    print(cvt(value)?, formatter, indent2)?;
94                    if idx + 1 < list.len() as usize {
95                        indent2.comma(formatter)?;
96                    }
97                }
98                indent.maybe_newline(formatter)?;
99                formatter.write_str("]")
100            }
101        }
102        dynamic_value::Reader::Struct(st) => {
103            let schema = st.get_schema();
104            let union_fields = cvt(schema.get_union_fields())?;
105            let non_union_fields = cvt(schema.get_non_union_fields())?;
106            if union_fields.len() + non_union_fields.len() == 0 {
107                return formatter.write_str("()");
108            }
109            formatter.write_str("(")?;
110            let indent2 = indent.next();
111            let mut union_field = match cvt(st.which())? {
112                None => None,
113                Some(field) => {
114                    // If it's not the default discriminant, then we always need to print it.
115                    if field.get_proto().get_discriminant_value() != 0 || cvt(st.has(field))? {
116                        Some(field)
117                    } else {
118                        None
119                    }
120                }
121            };
122            let mut first = true;
123            for field in non_union_fields {
124                if let Some(ff) = union_field {
125                    if ff.get_index() < field.get_index() {
126                        // It's time to print the union field.
127                        if first {
128                            first = false
129                        } else {
130                            indent2.comma(formatter)?;
131                        }
132                        indent2.maybe_newline(formatter)?;
133                        formatter.write_str(cvt(cvt(ff.get_proto().get_name())?.to_str())?)?;
134                        formatter.write_str(" = ")?;
135                        print(cvt(st.get(ff))?, formatter, indent2)?;
136                        union_field = None;
137                    }
138                }
139                if cvt(st.has(field))? {
140                    if first {
141                        first = false
142                    } else {
143                        indent2.comma(formatter)?;
144                    }
145                    indent2.maybe_newline(formatter)?;
146                    formatter.write_str(cvt(cvt(field.get_proto().get_name())?.to_str())?)?;
147                    formatter.write_str(" = ")?;
148                    print(cvt(st.get(field))?, formatter, indent2)?;
149                }
150            }
151            if let Some(ff) = union_field {
152                // Union field comes last.
153                if !first {
154                    indent2.comma(formatter)?;
155                }
156                indent2.maybe_newline(formatter)?;
157                formatter.write_str(cvt(cvt(ff.get_proto().get_name())?.to_str())?)?;
158                formatter.write_str(" = ")?;
159                print(cvt(st.get(ff))?, formatter, indent2)?;
160            }
161            indent.maybe_newline(formatter)?;
162            formatter.write_str(")")
163        }
164        dynamic_value::Reader::AnyPointer(_) => formatter.write_str("<opaque pointer>"),
165        dynamic_value::Reader::Capability(_) => formatter.write_str("<external capability>"),
166    }
167}
168
169impl fmt::Debug for dynamic_value::Reader<'_> {
170    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
171        let indent = if f.alternate() {
172            Indent::enabled()
173        } else {
174            Indent::no_indent()
175        };
176        print(*self, f, indent)
177    }
178}