netidx_value/
print.rs

1use crate::{parser, Value};
2use anyhow::{anyhow, Result};
3use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
4use bytes::BytesMut;
5use compact_str::CompactString;
6use escaping::Escape;
7use netidx_core::utils::pack;
8use rust_decimal::Decimal;
9use smallvec::smallvec;
10use std::{
11    cell::RefCell,
12    fmt::{self, Write},
13    ops::Deref,
14};
15
16struct DecimalFmt(Decimal);
17
18impl fmt::Display for DecimalFmt {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        // decimal, like float, must always end in '.', but the default
21        // decimal printer does not do that, so we have to fix it
22        thread_local! {
23            static BUF: RefCell<CompactString> = RefCell::new(CompactString::new(""));
24        }
25        BUF.with_borrow_mut(|buf| {
26            use std::fmt::Write;
27            buf.clear();
28            write!(buf, "{}", self.0)?;
29            if buf.contains('.') {
30                write!(f, "{buf}")
31            } else {
32                write!(f, "{buf}.")
33            }
34        })
35    }
36}
37
38/// A value reference that formats without type tags
39pub struct NakedValue<'a>(pub &'a Value);
40
41impl<'a> Deref for NakedValue<'a> {
42    type Target = Value;
43
44    fn deref(&self) -> &Self::Target {
45        self.0
46    }
47}
48
49impl<'a> fmt::Display for NakedValue<'a> {
50    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51        self.0.fmt_naked(f)
52    }
53}
54
55impl fmt::Display for Value {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        self.fmt_ext(f, &parser::VAL_ESC, true)
58    }
59}
60
61pub fn printf(f: &mut impl Write, fmt: &str, args: &[Value]) -> Result<usize> {
62    use compact_str::{format_compact, CompactString};
63    use fish_printf::{printf_c_locale, Arg, ToArg};
64    use rust_decimal::prelude::ToPrimitive;
65    use smallvec::SmallVec;
66    enum T<'a> {
67        Arg(Arg<'a>),
68        Index(usize),
69    }
70    let mut strings: SmallVec<[CompactString; 4]> = smallvec![];
71    let mut fish_args: SmallVec<[T; 8]> = smallvec![];
72    for v in args {
73        fish_args.push(match v {
74            Value::U8(v) => T::Arg(v.to_arg()),
75            Value::I8(v) => T::Arg(v.to_arg()),
76            Value::U16(v) => T::Arg(v.to_arg()),
77            Value::I16(v) => T::Arg(v.to_arg()),
78            Value::U32(v) | Value::V32(v) => T::Arg(v.to_arg()),
79            Value::I32(v) | Value::Z32(v) => T::Arg(v.to_arg()),
80            Value::U64(v) | Value::V64(v) => T::Arg(v.to_arg()),
81            Value::I64(v) | Value::Z64(v) => T::Arg(v.to_arg()),
82            Value::F32(v) => T::Arg(v.to_arg()),
83            Value::F64(v) => T::Arg(v.to_arg()),
84            Value::Decimal(v) => match v.to_f64() {
85                Some(f) => T::Arg(f.to_arg()),
86                None => {
87                    strings.push(format_compact!("{v}"));
88                    T::Index(strings.len() - 1)
89                }
90            },
91            Value::DateTime(v) => {
92                strings.push(format_compact!("{v}"));
93                T::Index(strings.len() - 1)
94            }
95            Value::Duration(v) => {
96                strings.push(format_compact!("{v:?}"));
97                T::Index(strings.len() - 1)
98            }
99            Value::String(s) => T::Arg(s.to_arg()),
100            Value::Bytes(b) => {
101                strings.push(format_compact!("{}", BASE64.encode(b)));
102                T::Index(strings.len() - 1)
103            }
104            Value::Bool(true) => T::Arg("true".to_arg()),
105            Value::Bool(false) => T::Arg("false".to_arg()),
106            Value::Null => T::Arg("null".to_arg()),
107            v @ (Value::Error(_)
108            | Value::Array(_)
109            | Value::Map(_)
110            | Value::Abstract(_)) => {
111                strings.push(format_compact!("{v}"));
112                T::Index(strings.len() - 1)
113            }
114        })
115    }
116    let mut fish_args: SmallVec<[Arg; 8]> = fish_args
117        .into_iter()
118        .map(|t| match t {
119            T::Arg(a) => a,
120            T::Index(i) => strings[i].to_arg(),
121        })
122        .collect();
123    printf_c_locale(f, fmt, &mut fish_args).map_err(|e| anyhow!(format!("{e:?}")))
124}
125
126impl Value {
127    pub fn to_string_naked(&self) -> String {
128        format!("{}", NakedValue(self))
129    }
130
131    pub fn fmt_naked(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        match self {
133            Value::U8(v) => write!(f, "{}", v),
134            Value::I8(v) => write!(f, "{}", v),
135            Value::U16(v) => write!(f, "{}", v),
136            Value::I16(v) => write!(f, "{}", v),
137            Value::U32(v) | Value::V32(v) => write!(f, "{}", v),
138            Value::I32(v) | Value::Z32(v) => write!(f, "{}", v),
139            Value::U64(v) | Value::V64(v) => write!(f, "{}", v),
140            Value::I64(v) | Value::Z64(v) => write!(f, "{}", v),
141            Value::F32(v) => write!(f, "{}", v),
142            Value::F64(v) => write!(f, "{}", v),
143            Value::Decimal(v) => write!(f, "{}", DecimalFmt(**v)),
144            Value::DateTime(v) => write!(f, "{}", v),
145            Value::Duration(v) => {
146                let v = v.as_secs_f64();
147                if v.fract() == 0. {
148                    write!(f, "{}.s", v)
149                } else {
150                    write!(f, "{}s", v)
151                }
152            }
153            Value::String(s) => write!(f, "\"{}\"", parser::VAL_ESC.escape(s)),
154            Value::Bytes(b) => write!(f, "{}", BASE64.encode(b)),
155            Value::Bool(true) => write!(f, "true"),
156            Value::Bool(false) => write!(f, "false"),
157            Value::Null => write!(f, "null"),
158            v @ (Value::Error(_)
159            | Value::Array(_)
160            | Value::Map(_)
161            | Value::Abstract(_)) => write!(f, "{}", v),
162        }
163    }
164
165    pub fn fmt_notyp(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        self.fmt_ext(f, &parser::VAL_ESC, false)
167    }
168
169    pub fn fmt_ext(
170        &self,
171        f: &mut fmt::Formatter<'_>,
172        esc: &Escape,
173        types: bool,
174    ) -> fmt::Result {
175        match self {
176            Value::U8(v) => {
177                if types {
178                    write!(f, "u8:{}", v)
179                } else {
180                    write!(f, "{}", v)
181                }
182            }
183            Value::I8(v) => {
184                if types {
185                    write!(f, "i8:{}", v)
186                } else {
187                    write!(f, "{}", v)
188                }
189            }
190            Value::U16(v) => {
191                if types {
192                    write!(f, "u16:{}", v)
193                } else {
194                    write!(f, "{}", v)
195                }
196            }
197            Value::I16(v) => {
198                if types {
199                    write!(f, "i16:{}", v)
200                } else {
201                    write!(f, "{}", v)
202                }
203            }
204            Value::U32(v) => {
205                if types {
206                    write!(f, "u32:{}", v)
207                } else {
208                    write!(f, "{}", v)
209                }
210            }
211            Value::V32(v) => {
212                if types {
213                    write!(f, "v32:{}", v)
214                } else {
215                    write!(f, "{}", v)
216                }
217            }
218            Value::I32(v) => {
219                if types {
220                    write!(f, "i32:{}", v)
221                } else {
222                    write!(f, "{}", v)
223                }
224            }
225            Value::Z32(v) => {
226                if types {
227                    write!(f, "z32:{}", v)
228                } else {
229                    write!(f, "{}", v)
230                }
231            }
232            Value::U64(v) => {
233                if types {
234                    write!(f, "u64:{}", v)
235                } else {
236                    write!(f, "{}", v)
237                }
238            }
239            Value::V64(v) => {
240                if types {
241                    write!(f, "v64:{}", v)
242                } else {
243                    write!(f, "{}", v)
244                }
245            }
246            Value::I64(v) => {
247                if types {
248                    write!(f, "i64:{}", v)
249                } else {
250                    write!(f, "{}", v)
251                }
252            }
253            Value::Z64(v) => {
254                if types {
255                    write!(f, "z64:{}", v)
256                } else {
257                    write!(f, "{}", v)
258                }
259            }
260            Value::F32(v) => {
261                let pfx = if types { "f32:" } else { "" };
262                if v.fract() == 0. {
263                    write!(f, "{}{}.", pfx, v)
264                } else {
265                    write!(f, "{}{}", pfx, v)
266                }
267            }
268            Value::F64(v) => {
269                let pfx = if types { "f64:" } else { "" };
270                if v.fract() == 0. {
271                    write!(f, "{}{}.", pfx, v)
272                } else {
273                    write!(f, "{}{}", pfx, v)
274                }
275            }
276            Value::Decimal(v) => {
277                if types {
278                    write!(f, "decimal:{}", DecimalFmt(**v))
279                } else {
280                    write!(f, "{}", DecimalFmt(**v))
281                }
282            }
283            Value::DateTime(v) => {
284                if types {
285                    write!(f, r#"datetime:"{}""#, v)
286                } else {
287                    write!(f, r#""{}""#, v)
288                }
289            }
290            Value::Duration(v) => {
291                let pfx = if types { "duration:" } else { "" };
292                let v = v.as_secs_f64();
293                if v.fract() == 0. {
294                    write!(f, r#"{}{}.s"#, pfx, v)
295                } else {
296                    write!(f, r#"{}{}s"#, pfx, v)
297                }
298            }
299            Value::String(s) => {
300                write!(f, r#""{}""#, esc.escape(&*s))
301            }
302            Value::Bytes(b) => {
303                let pfx = if types { "bytes:" } else { "" };
304                if b.is_empty() {
305                    write!(f, "{}null", pfx)
306                } else {
307                    write!(f, "{}{}", pfx, BASE64.encode(&*b))
308                }
309            }
310            Value::Bool(true) => write!(f, "true"),
311            Value::Bool(false) => write!(f, "false"),
312            Value::Null => write!(f, "null"),
313            Value::Error(v) => match &**v {
314                Value::String(s) => {
315                    write!(f, r#"error:"{}""#, esc.escape(&*s))
316                }
317                v => {
318                    write!(f, r#"error:{v}"#)
319                }
320            },
321            Value::Array(elts) => {
322                write!(f, "[")?;
323                for (i, v) in elts.iter().enumerate() {
324                    if i < elts.len() - 1 {
325                        v.fmt_ext(f, esc, types)?;
326                        write!(f, ", ")?
327                    } else {
328                        v.fmt_ext(f, esc, types)?
329                    }
330                }
331                write!(f, "]")
332            }
333            Value::Map(m) => {
334                write!(f, "{{")?;
335                for (i, (k, v)) in m.into_iter().enumerate() {
336                    k.fmt_ext(f, esc, types)?;
337                    write!(f, " => ")?;
338                    v.fmt_ext(f, esc, types)?;
339                    if i < m.len() - 1 {
340                        write!(f, ", ")?
341                    }
342                }
343                write!(f, "}}")
344            }
345            Value::Abstract(a) => {
346                let bytes = pack(a).unwrap_or_else(|_| BytesMut::new());
347                write!(f, "abstract:{}", BASE64.encode(&bytes))
348            }
349        }
350    }
351}