1#![cfg(feature = "std")]
2
3use std::collections::HashMap;
4use std::string;
5
6use crate::ir;
7use crate::visitor::Visitor;
8use crate::{Error, Result};
9
10pub fn print_source(buf: &[u8], ty: &ir::Ty, ty_defs: &[ir::TyDef]) -> Result<String> {
11 let mut printer = Printer {
12 indent: 0,
13 ty_defs: ty_defs.iter().map(|def| (&def.name, &def.ty)).collect(),
14 };
15
16 printer.visit(buf, ty)
17}
18
19#[derive(Clone)]
20struct Printer<'t> {
21 indent: usize,
22 ty_defs: HashMap<&'t ir::Path, &'t ir::Ty>,
23}
24
25const INDENT: usize = 2;
26
27impl<'t> Printer<'t> {
28 fn indent(&mut self) {
29 self.indent += INDENT;
30 }
31
32 fn deindent(&mut self) {
33 self.indent -= INDENT;
34 }
35
36 fn new_line(&self) -> String {
37 let mut r = "\n".to_string();
38 r += &" ".repeat(self.indent);
39 r
40 }
41}
42
43impl<'t, B> Visitor<'t, B> for Printer<'t>
44where
45 B: bytes::Buf + Clone,
46{
47 type Res = String;
48
49 fn get_ty(&self, name: &ir::Path) -> &'t ir::Ty {
50 self.ty_defs.get(name).unwrap()
51 }
52
53 fn visit(&mut self, buf: B, ty: &'t ir::Ty) -> Result<Self::Res, crate::Error> {
54 #[cfg(feature = "chrono")]
56 if let ir::TyKind::Ident(ty_ident) = &ty.kind
57 && ty_ident.0 == ["std", "Date"]
58 {
59 use crate::Decode;
60 let days = i32::decode(buf.chunk())?;
61
62 if let Some(date) = chrono::NaiveDate::from_epoch_days(days) {
63 return Ok(format!("@{date}"));
64 } else {
65 }
68 }
69 if let ir::TyKind::Ident(ty_ident) = &ty.kind
70 && ty_ident.0 == ["std", "Time"]
71 {
72 use crate::Decode;
73 let micros_t = i64::decode(buf.chunk())?;
74
75 let micros = (micros_t % 1000000).abs();
76 let sec_t = micros_t / 1000000;
77
78 let sec = (sec_t % 60).abs();
79 let min_t = sec_t / 60;
80
81 let min = (min_t % 60).abs();
82 let h_t = min_t / 60;
83
84 return Ok(format!("@{h_t:02}:{min:02}:{sec:02}.{micros:06}"));
85 }
86 if let ir::TyKind::Ident(ty_ident) = &ty.kind
87 && ty_ident.0 == ["std", "Timestamp"]
88 {
89 use crate::Decode;
90 let micros = i64::decode(buf.chunk())?;
91
92 if let Some(dt) = chrono::DateTime::from_timestamp_micros(micros) {
93 let dt = dt.to_rfc3339_opts(chrono::SecondsFormat::Micros, true);
94 return Ok(format!("@{}", dt.trim_end_matches('Z')));
95 } else {
96 }
98 }
99 if let ir::TyKind::Ident(ty_ident) = &ty.kind
100 && ty_ident.0 == ["std", "Decimal"]
101 {
102 use crate::Decode;
103 let val = i64::decode(buf.chunk())?;
104 return Ok(format!("{}.{:02}", val / 100, (val % 100).abs()));
105 }
106
107 let ty = Visitor::<B>::get_mat_ty(self, ty);
109
110 self.visit_concrete(buf, ty)
111 }
112
113 fn visit_bool(&mut self, v: bool) -> Result<Self::Res, Error> {
114 Ok(if v {
115 "true".to_string()
116 } else {
117 "false".to_string()
118 })
119 }
120
121 fn visit_int8(&mut self, v: i8) -> Result<Self::Res, Error> {
122 Ok(format!("{v}"))
123 }
124 fn visit_int16(&mut self, v: i16) -> Result<Self::Res, Error> {
125 Ok(format!("{v}"))
126 }
127 fn visit_int32(&mut self, v: i32) -> Result<Self::Res, Error> {
128 Ok(format!("{v}"))
129 }
130 fn visit_int64(&mut self, v: i64) -> Result<Self::Res, Error> {
131 Ok(format!("{v}"))
132 }
133 fn visit_uint8(&mut self, v: u8) -> Result<Self::Res, Error> {
134 Ok(format!("{v}"))
135 }
136 fn visit_uint16(&mut self, v: u16) -> Result<Self::Res, Error> {
137 Ok(format!("{v}"))
138 }
139 fn visit_uint32(&mut self, v: u32) -> Result<Self::Res, Error> {
140 Ok(format!("{v}"))
141 }
142 fn visit_uint64(&mut self, v: u64) -> Result<Self::Res, Error> {
143 Ok(format!("{v}"))
144 }
145 fn visit_float32(&mut self, v: f32) -> Result<Self::Res, Error> {
146 Ok(format!("{v:#?}"))
147 }
148 fn visit_float64(&mut self, v: f64) -> Result<Self::Res, Error> {
149 Ok(format!("{v:#?}"))
150 }
151
152 fn visit_text(&mut self, mut contents: B, len: usize) -> Result<Self::Res, Error> {
153 let mut buf = vec![0; len];
154 contents.copy_to_slice(&mut buf);
155
156 let s = string::String::from_utf8(buf).map_err(|_| Error::InvalidData)?;
157 Ok(quote_text(&s))
158 }
159
160 fn visit_tuple(
161 &mut self,
162 fields: impl Iterator<Item = (B, &'t ir::TyTupleField)>,
163 ) -> Result<Self::Res, Error> {
164 let mut r = "{".to_string();
165 let mut had_a_field = false;
166 self.indent();
167 for (field, ty) in fields {
168 had_a_field = true;
169 r += &self.new_line();
170
171 if let Some(name) = &ty.name {
172 r += name;
173 r += " = ";
174 }
175
176 r += &self.visit(field, &ty.ty)?;
177 r += ",";
178 }
179 self.deindent();
180 if had_a_field {
181 r += &self.new_line();
182 }
183 r += "}";
184 Ok(r)
185 }
186
187 fn visit_array(
188 &mut self,
189 items: impl Iterator<Item = B>,
190 ty_items: &'t ir::Ty,
191 ) -> Result<Self::Res, Error> {
192 let mut r = "[".to_string();
193
194 let mut items = items.peekable();
195 if items.peek().is_some() {
196 self.indent();
197 for item in items {
198 r += &self.new_line();
199 r += &self.visit(item, ty_items)?;
200 r += ",";
201 }
202 self.deindent();
203 r += &self.new_line();
204 }
205
206 r += "]";
207 Ok(r)
208 }
209
210 fn visit_enum(
211 &mut self,
212 tag: usize,
213 inner: B,
214 ty_variants: &'t [ir::TyEnumVariant],
215 ) -> Result<Self::Res, Error> {
216 let variant = ty_variants.get(tag).ok_or(Error::InvalidData)?;
217
218 let mut r = variant.name.to_string();
219
220 let is_unit = variant.ty.kind.as_tuple().is_some_and(|x| x.is_empty());
221 if !is_unit {
222 r += "(";
223 r += &self.visit(inner, &variant.ty)?;
224 r += ")";
225 }
226
227 Ok(r)
228 }
229}
230
231fn quote_text(text: &str) -> String {
232 let mut result = String::new();
233 result.push('"');
234
235 for c in text.chars() {
236 match c {
237 '\n' => result.push_str("\\n"),
238 '\r' => result.push_str("\\r"),
239 '\t' => result.push_str("\\t"),
240 '\\' => result.push_str("\\\\"),
241 '"' => result.push_str("\\\""),
242 c if c.is_ascii_control() => {
243 let hex = format!("\\x{:02X}", c as u8);
244 result.push_str(&hex);
245 }
246 _ => result.push(c),
247 }
248 }
249 result.push('"');
250 result
251}