1use std::io::Read;
2use std::io::Write;
3
4#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6#[serde(untagged)]
7pub enum DecodedValue {
8 Scalar(DecodedScalar),
9 Table(std::collections::HashMap<String, DecodedValue>),
10 Array(Vec<DecodedValue>),
11}
12
13impl DecodedValue {
14 pub fn from_slice(v: &[u8]) -> Result<Self, crate::Error> {
15 serde_json::from_slice(v).map_err(|e| {
16 crate::Error::new(format!(
17 "failed decoding: {}\n```json\n{}\n```",
18 e,
19 String::from_utf8_lossy(v)
20 ))
21 })
22 }
23
24 pub fn to_string_pretty(&self) -> Result<String, crate::Error> {
25 serde_json::to_string_pretty(self).map_err(crate::Error::new)
26 }
27
28 pub fn from_stdin() -> Result<Self, crate::Error> {
30 let mut buf = Vec::new();
31 std::io::stdin()
32 .read_to_end(&mut buf)
33 .map_err(crate::Error::new)?;
34 Self::from_slice(&buf)
35 }
36
37 pub fn into_stdout(&self) -> Result<(), crate::Error> {
39 let s = self.to_string_pretty()?;
40 std::io::stdout()
41 .write_all(s.as_bytes())
42 .map_err(crate::Error::new)
43 }
44}
45
46#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
48#[serde(rename_all = "kebab-case")]
49#[serde(tag = "type", content = "value")]
50pub enum DecodedScalar {
51 String(String),
52 Integer(String),
53 Float(String),
54 Bool(String),
55 Datetime(String),
56 DatetimeLocal(String),
57 DateLocal(String),
58 TimeLocal(String),
59}
60
61impl DecodedScalar {
62 pub fn as_str(&self) -> &str {
63 match self {
64 DecodedScalar::String(v)
65 | DecodedScalar::Integer(v)
66 | DecodedScalar::Float(v)
67 | DecodedScalar::Bool(v)
68 | DecodedScalar::Datetime(v)
69 | DecodedScalar::DatetimeLocal(v)
70 | DecodedScalar::DateLocal(v)
71 | DecodedScalar::TimeLocal(v) => v.as_str(),
72 }
73 }
74}
75
76impl<'a> From<&'a str> for DecodedScalar {
77 fn from(other: &'a str) -> Self {
78 DecodedScalar::String(other.to_owned())
79 }
80}
81
82impl<'a> From<&'a String> for DecodedScalar {
83 fn from(other: &'a String) -> Self {
84 DecodedScalar::String(other.clone())
85 }
86}
87
88impl From<String> for DecodedScalar {
89 fn from(other: String) -> Self {
90 DecodedScalar::String(other)
91 }
92}
93
94impl From<i64> for DecodedScalar {
95 fn from(other: i64) -> Self {
96 DecodedScalar::Integer(other.to_string())
97 }
98}
99
100impl From<f64> for DecodedScalar {
101 fn from(other: f64) -> Self {
102 let s = if other.is_nan() {
103 "nan".to_owned()
104 } else if other.is_infinite() && other.is_sign_negative() {
105 "-inf".to_owned()
106 } else if other.is_infinite() && other.is_sign_positive() {
107 "inf".to_owned()
108 } else {
109 let mut buffer = ryu::Buffer::new();
110 let printed = buffer.format(other);
111 printed.to_owned()
112 };
113 DecodedScalar::Float(s)
114 }
115}
116
117impl From<bool> for DecodedScalar {
118 fn from(other: bool) -> Self {
119 DecodedScalar::Bool(other.to_string())
120 }
121}
122
123impl PartialEq for DecodedScalar {
124 fn eq(&self, other: &Self) -> bool {
125 #[allow(clippy::if_same_then_else)]
126 match (self, other) {
127 (DecodedScalar::String(s), DecodedScalar::String(o)) => s == o,
128 (DecodedScalar::Integer(s), DecodedScalar::Integer(o)) => s == o,
129 (DecodedScalar::Float(s), DecodedScalar::Float(o)) => {
130 if s == "inf" && o == "+inf" {
131 true
132 } else if s == "+inf" && o == "inf" {
133 true
134 } else if s == "nan" && o == "nan" {
135 true
136 } else {
137 let s = s.parse::<f64>().unwrap();
138 let o = o.parse::<f64>().unwrap();
139 s == o
140 }
141 }
142 (DecodedScalar::Bool(s), DecodedScalar::Bool(o)) => s == o,
143 (DecodedScalar::Datetime(s), DecodedScalar::Datetime(o)) => {
144 parse_date_time(s) == parse_date_time(o)
145 }
146 (DecodedScalar::DatetimeLocal(s), DecodedScalar::DatetimeLocal(o)) => {
147 parse_date_time_local(s) == parse_date_time_local(o)
148 }
149 (DecodedScalar::DateLocal(s), DecodedScalar::DateLocal(o)) => {
150 parse_date_local(s) == parse_date_local(o)
151 }
152 (DecodedScalar::TimeLocal(s), DecodedScalar::TimeLocal(o)) => {
153 parse_time_local(s) == parse_time_local(o)
154 }
155 (_, _) => false,
156 }
157 }
158}
159
160fn parse_date_time(s: &str) -> chrono::DateTime<chrono::FixedOffset> {
161 match normalize_datetime(s).parse() {
162 Ok(d) => d,
163 Err(err) => panic!("Failed to parse {s:?}: {err}"),
164 }
165}
166
167fn parse_date_time_local(s: &str) -> chrono::NaiveDateTime {
168 match normalize_datetime(s).parse() {
169 Ok(d) => d,
170 Err(err) => panic!("Failed to parse {s:?}: {err}"),
171 }
172}
173
174fn parse_date_local(s: &str) -> chrono::NaiveDate {
175 match s.parse() {
176 Ok(d) => d,
177 Err(err) => panic!("Failed to parse {s:?}: {err}"),
178 }
179}
180
181fn parse_time_local(s: &str) -> chrono::NaiveTime {
182 match s.parse() {
183 Ok(d) => d,
184 Err(err) => panic!("Failed to parse {s:?}: {err}"),
185 }
186}
187
188fn normalize_datetime(s: &str) -> String {
189 s.chars()
190 .map(|c| match c {
191 ' ' => 'T',
192 't' => 'T',
193 'z' => 'Z',
194 _ => c,
195 })
196 .collect()
197}
198
199impl Eq for DecodedScalar {}
200
201#[cfg(test)]
202mod test {
203 use super::*;
204
205 #[test]
206 fn string_equality() {
207 assert_eq!(DecodedScalar::from("foo"), DecodedScalar::from("foo"));
208 assert_ne!(DecodedScalar::from("foo"), DecodedScalar::from("bar"));
209 assert_ne!(DecodedScalar::from("42"), DecodedScalar::from(42));
210 assert_ne!(DecodedScalar::from("true"), DecodedScalar::from(true));
211 }
212
213 #[test]
214 fn integer_equality() {
215 assert_eq!(DecodedScalar::from(42), DecodedScalar::from(42));
216 assert_ne!(DecodedScalar::from(42), DecodedScalar::from(21));
217 assert_ne!(DecodedScalar::from(42), DecodedScalar::from("42"));
218 }
219
220 #[test]
221 fn float_equality() {
222 assert_eq!(DecodedScalar::from(42.0), DecodedScalar::from(42.0));
223 assert_ne!(DecodedScalar::from(42.0), DecodedScalar::from(21.0));
224 assert_ne!(DecodedScalar::from(42.0), DecodedScalar::from("42.0"));
225 }
226
227 #[test]
228 fn nan_equality() {
229 assert_eq!(DecodedScalar::from(f64::NAN), DecodedScalar::from(f64::NAN));
230 assert_eq!(
231 DecodedScalar::from(f64::NAN),
232 DecodedScalar::Float("nan".to_owned())
233 );
234 assert_ne!(DecodedScalar::from(f64::NAN), DecodedScalar::from("nan"));
235 }
236
237 #[test]
238 fn inf_equality() {
239 assert_eq!(
240 DecodedScalar::from(f64::INFINITY),
241 DecodedScalar::from(f64::INFINITY)
242 );
243 assert_ne!(
244 DecodedScalar::from(f64::INFINITY),
245 DecodedScalar::from(f64::NEG_INFINITY)
246 );
247 assert_eq!(
248 DecodedScalar::from(f64::INFINITY),
249 DecodedScalar::Float("inf".to_owned())
250 );
251 assert_eq!(
252 DecodedScalar::from(f64::INFINITY),
253 DecodedScalar::Float("+inf".to_owned())
254 );
255 assert_ne!(
256 DecodedScalar::from(f64::INFINITY),
257 DecodedScalar::from("inf")
258 );
259 }
260
261 #[test]
262 fn float_exp_equality() {
263 assert_eq!(DecodedScalar::from(3.0e14), DecodedScalar::from(3.0e14));
264 assert_eq!(
265 DecodedScalar::from(3.0e14),
266 DecodedScalar::Float("3.0e14".to_owned())
267 );
268 }
269
270 #[test]
271 fn float_binary_equality() {
272 #![allow(clippy::excessive_precision)]
273
274 assert_eq!(
277 DecodedScalar::from(3141.5927),
278 DecodedScalar::Float("3141.5927".to_owned())
279 );
280 assert_eq!(
281 DecodedScalar::from(3141.59270000000015),
282 DecodedScalar::Float("3141.5927".to_owned())
283 );
284 }
285
286 #[test]
287 fn neg_inf_equality() {
288 assert_eq!(
289 DecodedScalar::from(f64::NEG_INFINITY),
290 DecodedScalar::from(f64::NEG_INFINITY)
291 );
292 assert_ne!(
293 DecodedScalar::from(f64::NEG_INFINITY),
294 DecodedScalar::from(f64::INFINITY)
295 );
296 assert_eq!(
297 DecodedScalar::from(f64::NEG_INFINITY),
298 DecodedScalar::Float("-inf".to_owned())
299 );
300 assert_ne!(
301 DecodedScalar::from(f64::NEG_INFINITY),
302 DecodedScalar::from("-inf")
303 );
304 }
305
306 #[test]
307 fn bool_equality() {
308 assert_eq!(DecodedScalar::from(true), DecodedScalar::from(true));
309 assert_ne!(DecodedScalar::from(true), DecodedScalar::from(false));
310 assert_ne!(DecodedScalar::from(true), DecodedScalar::from("true"));
311 }
312
313 #[test]
314 fn datetime_equality() {
315 assert_eq!(
316 DecodedScalar::Datetime("1987-07-05 17:45:00Z".to_owned()),
317 DecodedScalar::Datetime("1987-07-05 17:45:00Z".to_owned())
318 );
319 assert_eq!(
320 DecodedScalar::Datetime("1987-07-05T17:45:56.123456Z".to_owned()),
321 DecodedScalar::Datetime("1987-07-05T17:45:56.123456Z".to_owned()),
322 );
323 assert_ne!(
324 DecodedScalar::Datetime("1987-07-05 17:45:00Z".to_owned()),
325 DecodedScalar::Datetime("2000-07-05 17:45:00Z".to_owned())
326 );
327 assert_eq!(
328 DecodedScalar::Datetime("1987-07-05t17:45:00z".to_owned()),
329 DecodedScalar::Datetime("1987-07-05 17:45:00Z".to_owned())
330 );
331 assert_ne!(
332 DecodedScalar::Datetime("1987-07-05 17:45:00Z".to_owned()),
333 DecodedScalar::from("1987-07-05 17:45:00Z")
334 );
335 }
336
337 #[test]
338 fn datetime_local_equality() {
339 assert_eq!(
340 DecodedScalar::DatetimeLocal("1987-07-05 17:45:00".to_owned()),
341 DecodedScalar::DatetimeLocal("1987-07-05 17:45:00".to_owned())
342 );
343 assert_eq!(
344 DecodedScalar::DatetimeLocal("1987-07-05 17:45:00.444".to_owned()),
345 DecodedScalar::DatetimeLocal("1987-07-05 17:45:00.444".to_owned())
346 );
347 assert_ne!(
348 DecodedScalar::DatetimeLocal("1987-07-05 17:45:00".to_owned()),
349 DecodedScalar::DatetimeLocal("2000-07-05 17:45:00".to_owned())
350 );
351 assert_eq!(
352 DecodedScalar::DatetimeLocal("1987-07-05t17:45:00".to_owned()),
353 DecodedScalar::DatetimeLocal("1987-07-05 17:45:00".to_owned())
354 );
355 assert_ne!(
356 DecodedScalar::DatetimeLocal("1987-07-05 17:45:00".to_owned()),
357 DecodedScalar::from("1987-07-05 17:45:00")
358 );
359 }
360
361 #[test]
362 fn date_local_equality() {
363 assert_eq!(
364 DecodedScalar::DateLocal("1987-07-05".to_owned()),
365 DecodedScalar::DateLocal("1987-07-05".to_owned())
366 );
367 assert_ne!(
368 DecodedScalar::DateLocal("1987-07-05".to_owned()),
369 DecodedScalar::DateLocal("2000-07-05".to_owned())
370 );
371 assert_ne!(
372 DecodedScalar::DateLocal("1987-07-05".to_owned()),
373 DecodedScalar::from("1987-07-05")
374 );
375 }
376
377 #[test]
378 fn time_local_equality() {
379 assert_eq!(
380 DecodedScalar::TimeLocal("17:45:00".to_owned()),
381 DecodedScalar::TimeLocal("17:45:00".to_owned())
382 );
383 assert_eq!(
384 DecodedScalar::TimeLocal("17:45:00.444".to_owned()),
385 DecodedScalar::TimeLocal("17:45:00.444".to_owned())
386 );
387 assert_ne!(
388 DecodedScalar::TimeLocal("17:45:00".to_owned()),
389 DecodedScalar::TimeLocal("19:45:00".to_owned())
390 );
391 assert_ne!(
392 DecodedScalar::TimeLocal("17:45:00".to_owned()),
393 DecodedScalar::from("17:45:00")
394 );
395 }
396}