netidx_value/
print.rs

1use crate::{parser, Value};
2use anyhow::{anyhow, Result};
3use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
4use escaping::Escape;
5use smallvec::smallvec;
6use std::{
7    fmt::{self, Write},
8    ops::Deref,
9};
10
11/// A value reference that formats without type tags
12pub struct NakedValue<'a>(pub &'a Value);
13
14impl<'a> Deref for NakedValue<'a> {
15    type Target = Value;
16
17    fn deref(&self) -> &Self::Target {
18        self.0
19    }
20}
21
22impl<'a> fmt::Display for NakedValue<'a> {
23    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24        self.0.fmt_naked(f)
25    }
26}
27
28impl fmt::Display for Value {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        self.fmt_ext(f, &parser::VAL_ESC, true)
31    }
32}
33
34pub fn printf(f: &mut impl Write, fmt: &str, args: &[Value]) -> Result<usize> {
35    use compact_str::{format_compact, CompactString};
36    use fish_printf::{printf_c_locale, Arg, ToArg};
37    use rust_decimal::prelude::ToPrimitive;
38    use smallvec::SmallVec;
39    enum T<'a> {
40        Arg(Arg<'a>),
41        Index(usize),
42    }
43    let mut strings: SmallVec<[CompactString; 4]> = smallvec![];
44    let mut fish_args: SmallVec<[T; 8]> = smallvec![];
45    for v in args {
46        fish_args.push(match v {
47            Value::U32(v) | Value::V32(v) => T::Arg(v.to_arg()),
48            Value::I32(v) | Value::Z32(v) => T::Arg(v.to_arg()),
49            Value::U64(v) | Value::V64(v) => T::Arg(v.to_arg()),
50            Value::I64(v) | Value::Z64(v) => T::Arg(v.to_arg()),
51            Value::F32(v) => T::Arg(v.to_arg()),
52            Value::F64(v) => T::Arg(v.to_arg()),
53            Value::Decimal(v) => match v.to_f64() {
54                Some(f) => T::Arg(f.to_arg()),
55                None => {
56                    strings.push(format_compact!("{v}"));
57                    T::Index(strings.len() - 1)
58                }
59            },
60            Value::DateTime(v) => {
61                strings.push(format_compact!("{v}"));
62                T::Index(strings.len() - 1)
63            }
64            Value::Duration(v) => {
65                strings.push(format_compact!("{v:?}"));
66                T::Index(strings.len() - 1)
67            }
68            Value::String(s) => T::Arg(s.to_arg()),
69            Value::Bytes(b) => {
70                strings.push(format_compact!("{}", BASE64.encode(b)));
71                T::Index(strings.len() - 1)
72            }
73            Value::Bool(true) => T::Arg("true".to_arg()),
74            Value::Bool(false) => T::Arg("false".to_arg()),
75            Value::Null => T::Arg("null".to_arg()),
76            v @ Value::Error(_) => {
77                strings.push(format_compact!("{v}"));
78                T::Index(strings.len() - 1)
79            }
80            v @ Value::Array(_) => {
81                strings.push(format_compact!("{v}"));
82                T::Index(strings.len() - 1)
83            }
84        })
85    }
86    let mut fish_args: SmallVec<[Arg; 8]> = fish_args
87        .into_iter()
88        .map(|t| match t {
89            T::Arg(a) => a,
90            T::Index(i) => strings[i].to_arg(),
91        })
92        .collect();
93    printf_c_locale(f, fmt, &mut fish_args).map_err(|e| anyhow!(format!("{e:?}")))
94}
95
96impl Value {
97    pub fn to_string_naked(&self) -> String {
98        format!("{}", NakedValue(self))
99    }
100
101    pub fn fmt_naked(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match self {
103            Value::U32(v) | Value::V32(v) => write!(f, "{}", v),
104            Value::I32(v) | Value::Z32(v) => write!(f, "{}", v),
105            Value::U64(v) | Value::V64(v) => write!(f, "{}", v),
106            Value::I64(v) | Value::Z64(v) => write!(f, "{}", v),
107            Value::F32(v) => write!(f, "{}", v),
108            Value::F64(v) => write!(f, "{}", v),
109            Value::Decimal(v) => write!(f, "{}", v),
110            Value::DateTime(v) => write!(f, "{}", v),
111            Value::Duration(v) => {
112                let v = v.as_secs_f64();
113                if v.fract() == 0. {
114                    write!(f, "{}.s", v)
115                } else {
116                    write!(f, "{}s", v)
117                }
118            }
119            Value::String(s) => write!(f, "\"{}\"", parser::VAL_ESC.escape(s)),
120            Value::Bytes(b) => write!(f, "{}", BASE64.encode(b)),
121            Value::Bool(true) => write!(f, "true"),
122            Value::Bool(false) => write!(f, "false"),
123            Value::Null => write!(f, "null"),
124            v @ Value::Error(_) => write!(f, "{}", v),
125            v @ Value::Array(_) => write!(f, "{}", v),
126        }
127    }
128
129    pub fn fmt_notyp(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        self.fmt_ext(f, &parser::VAL_ESC, false)
131    }
132
133    pub fn fmt_ext(
134        &self,
135        f: &mut fmt::Formatter<'_>,
136        esc: &Escape,
137        types: bool,
138    ) -> fmt::Result {
139        match self {
140            Value::U32(v) => {
141                if types {
142                    write!(f, "u32:{}", v)
143                } else {
144                    write!(f, "{}", v)
145                }
146            }
147            Value::V32(v) => {
148                if types {
149                    write!(f, "v32:{}", v)
150                } else {
151                    write!(f, "{}", v)
152                }
153            }
154            Value::I32(v) => {
155                if types {
156                    write!(f, "i32:{}", v)
157                } else {
158                    write!(f, "{}", v)
159                }
160            }
161            Value::Z32(v) => {
162                if types {
163                    write!(f, "z32:{}", v)
164                } else {
165                    write!(f, "{}", v)
166                }
167            }
168            Value::U64(v) => {
169                if types {
170                    write!(f, "u64:{}", v)
171                } else {
172                    write!(f, "{}", v)
173                }
174            }
175            Value::V64(v) => {
176                if types {
177                    write!(f, "v64:{}", v)
178                } else {
179                    write!(f, "{}", v)
180                }
181            }
182            Value::I64(v) => {
183                if types {
184                    write!(f, "i64:{}", v)
185                } else {
186                    write!(f, "{}", v)
187                }
188            }
189            Value::Z64(v) => {
190                if types {
191                    write!(f, "z64:{}", v)
192                } else {
193                    write!(f, "{}", v)
194                }
195            }
196            Value::F32(v) => {
197                let pfx = if types { "f32:" } else { "" };
198                if v.fract() == 0. {
199                    write!(f, "{}{}.", pfx, v)
200                } else {
201                    write!(f, "{}{}", pfx, v)
202                }
203            }
204            Value::F64(v) => {
205                let pfx = if types { "f64:" } else { "" };
206                if v.fract() == 0. {
207                    write!(f, "{}{}.", pfx, v)
208                } else {
209                    write!(f, "{}{}", pfx, v)
210                }
211            }
212            Value::Decimal(v) => {
213                if types {
214                    write!(f, "decimal:{}", v)
215                } else {
216                    write!(f, "{}", v)
217                }
218            }
219            Value::DateTime(v) => {
220                if types {
221                    write!(f, r#"datetime:"{}""#, v)
222                } else {
223                    write!(f, r#""{}""#, v)
224                }
225            }
226            Value::Duration(v) => {
227                let pfx = if types { "duration:" } else { "" };
228                let v = v.as_secs_f64();
229                if v.fract() == 0. {
230                    write!(f, r#"{}{}.s"#, pfx, v)
231                } else {
232                    write!(f, r#"{}{}s"#, pfx, v)
233                }
234            }
235            Value::String(s) => {
236                write!(f, r#""{}""#, esc.escape(&*s))
237            }
238            Value::Bytes(b) => {
239                let pfx = if types { "bytes:" } else { "" };
240                write!(f, "{}{}", pfx, BASE64.encode(&*b))
241            }
242            Value::Bool(true) => write!(f, "true"),
243            Value::Bool(false) => write!(f, "false"),
244            Value::Null => write!(f, "null"),
245            Value::Error(v) => {
246                write!(f, r#"error:"{}""#, esc.escape(&*v))
247            }
248            Value::Array(elts) => {
249                write!(f, "[")?;
250                for (i, v) in elts.iter().enumerate() {
251                    if i < elts.len() - 1 {
252                        v.fmt_ext(f, esc, types)?;
253                        write!(f, ", ")?
254                    } else {
255                        v.fmt_ext(f, esc, types)?
256                    }
257                }
258                write!(f, "]")
259            }
260        }
261    }
262}