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
11pub 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}