1use crate::Rule;
2use indexmap::IndexMap;
3use log::trace;
4use pest::iterators::{Pair, Pairs};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::fmt::{Display, Formatter};
9#[cfg(feature = "temporal")]
10use std::ops::{Add, Deref, Sub};
11
12#[cfg(feature = "temporal")]
14#[derive(Debug, Clone)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16#[cfg_attr(feature = "serde", serde(transparent))]
17pub struct DateTimeValue {
18 value: chrono::DateTime<chrono::FixedOffset>,
19 #[cfg_attr(feature = "serde", serde(skip))]
20 timezone: Option<TimezoneValue>,
21 #[cfg_attr(feature = "serde", serde(skip))]
22 zone_name: Option<String>,
23}
24
25#[cfg(feature = "temporal")]
26impl DateTimeValue {
27 pub fn fixed(value: chrono::DateTime<chrono::FixedOffset>) -> Self {
28 Self { value, timezone: None, zone_name: None }
29 }
30
31 pub fn zoned(
32 value: chrono::DateTime<chrono_tz::Tz>,
33 timezone: impl Into<TimezoneValue>,
34 ) -> Self {
35 Self {
36 value: value.fixed_offset(),
37 timezone: Some(timezone.into()),
38 zone_name: None,
39 }
40 }
41
42 pub(crate) fn with_timezone(&self, timezone: TimezoneValue) -> Self {
43 Self::zoned(self.value.with_timezone(&timezone.timezone()), timezone)
44 }
45
46 pub(crate) fn timezone(&self) -> Option<TimezoneValue> {
47 self.timezone
48 }
49
50 pub(crate) fn with_zone_name(mut self, zone_name: String) -> Self {
51 self.zone_name = Some(zone_name);
52 self
53 }
54
55 pub fn checked_add_signed(mut self, duration: chrono::Duration) -> Option<Self> {
56 self.value = self.value.checked_add_signed(duration)?;
57 self.refresh_timezone();
58 Some(self)
59 }
60
61 pub fn checked_sub_signed(mut self, duration: chrono::Duration) -> Option<Self> {
62 self.value = self.value.checked_sub_signed(duration)?;
63 self.refresh_timezone();
64 Some(self)
65 }
66
67 fn refresh_timezone(&mut self) {
68 if let Some(timezone) = self.timezone {
69 self.value = self.value.with_timezone(&timezone.timezone()).fixed_offset();
70 }
71 }
72
73 pub(crate) fn zone_name(&self) -> String {
74 if let Some(zone_name) = &self.zone_name {
75 return zone_name.clone();
76 }
77 if let Some(timezone) = self.timezone {
78 self.value
79 .with_timezone(&timezone.timezone())
80 .format("%Z")
81 .to_string()
82 } else if self.value.offset().local_minus_utc() == 0 {
83 "UTC".to_string()
84 } else {
85 self.value.format("%z").to_string()
86 }
87 }
88}
89
90#[cfg(feature = "temporal")]
91impl From<chrono::DateTime<chrono::FixedOffset>> for DateTimeValue {
92 fn from(value: chrono::DateTime<chrono::FixedOffset>) -> Self {
93 Self::fixed(value)
94 }
95}
96
97#[cfg(feature = "temporal")]
98impl Deref for DateTimeValue {
99 type Target = chrono::DateTime<chrono::FixedOffset>;
100
101 fn deref(&self) -> &Self::Target {
102 &self.value
103 }
104}
105
106#[cfg(feature = "temporal")]
107impl PartialEq for DateTimeValue {
108 fn eq(&self, other: &Self) -> bool {
109 self.value == other.value
110 }
111}
112
113#[cfg(feature = "temporal")]
114impl PartialOrd for DateTimeValue {
115 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
116 self.value.partial_cmp(&other.value)
117 }
118}
119
120#[cfg(feature = "temporal")]
121impl Add<chrono::Duration> for DateTimeValue {
122 type Output = Self;
123
124 fn add(mut self, duration: chrono::Duration) -> Self::Output {
125 self.value += duration;
126 self.refresh_timezone();
127 self
128 }
129}
130
131#[cfg(feature = "temporal")]
132impl Sub<chrono::Duration> for DateTimeValue {
133 type Output = Self;
134
135 fn sub(mut self, duration: chrono::Duration) -> Self::Output {
136 self.value -= duration;
137 self.refresh_timezone();
138 self
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
145#[cfg(feature = "temporal")]
146#[cfg_attr(feature = "serde", serde(transparent))]
147pub struct TimezoneValue {
148 timezone: chrono_tz::Tz,
149 #[cfg_attr(feature = "serde", serde(skip))]
150 local: bool,
151}
152
153#[cfg(feature = "temporal")]
154impl TimezoneValue {
155 pub fn named(timezone: chrono_tz::Tz) -> Self {
157 Self { timezone, local: false }
158 }
159
160 pub fn local() -> Result<Self, String> {
162 let timezone = match std::env::var_os("TZ") {
163 Some(timezone) => timezone_from_env(&timezone),
164 None => {
165 iana_time_zone::get_timezone()
166 .map_err(|error| error.to_string())?
167 .parse::<chrono_tz::Tz>()
168 .map_err(|error| error.to_string())
169 }?,
170 };
171 Ok(Self { timezone, local: true })
172 }
173
174 #[cfg(test)]
175 pub(crate) fn local_with_timezone(timezone: chrono_tz::Tz) -> Self {
176 Self { timezone, local: true }
177 }
178
179 pub fn timezone(self) -> chrono_tz::Tz {
181 self.timezone
182 }
183
184 pub fn name(self) -> &'static str {
186 if self.local {
187 "Local"
188 } else {
189 self.timezone.name()
190 }
191 }
192
193 pub fn is_local(self) -> bool {
195 self.local
196 }
197}
198
199#[cfg(feature = "temporal")]
200fn timezone_from_env(value: &std::ffi::OsStr) -> chrono_tz::Tz {
201 value
202 .to_str()
203 .and_then(|timezone| {
204 timezone
205 .strip_prefix(':')
206 .unwrap_or(timezone)
207 .parse::<chrono_tz::Tz>()
208 .ok()
209 })
210 .unwrap_or(chrono_tz::UTC)
211}
212
213#[cfg(feature = "temporal")]
214impl From<chrono_tz::Tz> for TimezoneValue {
215 fn from(timezone: chrono_tz::Tz) -> Self {
216 Self::named(timezone)
217 }
218}
219
220#[cfg(feature = "temporal")]
221impl Display for TimezoneValue {
222 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
223 f.write_str(self.name())
224 }
225}
226
227#[cfg(all(test, feature = "temporal"))]
228mod timezone_tests {
229 use super::*;
230
231 #[test]
232 fn empty_and_invalid_tz_environment_values_use_utc() {
233 assert_eq!(timezone_from_env(std::ffi::OsStr::new("")), chrono_tz::UTC);
234 assert_eq!(
235 timezone_from_env(std::ffi::OsStr::new("not-a-timezone")),
236 chrono_tz::UTC
237 );
238 }
239
240 #[test]
241 fn tz_environment_value_accepts_go_colon_prefix() {
242 assert_eq!(
243 timezone_from_env(std::ffi::OsStr::new(":America/New_York")),
244 chrono_tz::America::New_York
245 );
246 }
247}
248
249#[cfg(feature = "temporal")]
250impl Sub for DateTimeValue {
251 type Output = chrono::Duration;
252
253 fn sub(self, other: Self) -> Self::Output {
254 self.value - other.value
255 }
256}
257
258#[derive(Debug, Default, Clone, PartialEq)]
260#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
261#[cfg_attr(feature = "serde", serde(untagged))]
262pub enum Value {
263 Integer(i64),
264 Bool(bool),
265 Float(f64),
266 #[default]
267 Nil,
268 String(String),
269 #[cfg(feature = "temporal")]
270 DateTime(DateTimeValue),
271 #[cfg(feature = "temporal")]
272 Duration(i64),
273 #[cfg(feature = "temporal")]
274 Timezone(TimezoneValue),
275 #[cfg(feature = "temporal")]
276 Month(u32),
277 #[cfg(feature = "temporal")]
278 Weekday(u32),
279 Array(Vec<Value>),
280 Bytes(Vec<u8>),
282 Map(IndexMap<String, Value>),
283 KeyedMap(Vec<(Value, Value)>),
285}
286
287impl Value {
288 pub(crate) fn parse_integer(value: &str) -> std::result::Result<i64, std::num::ParseIntError> {
289 let value = value.replace('_', "");
290 let (digits, radix) = match value.as_bytes() {
291 [b'0', b'x' | b'X', ..] => (&value[2..], 16),
292 [b'0', b'o' | b'O', ..] => (&value[2..], 8),
293 [b'0', b'b' | b'B', ..] => (&value[2..], 2),
294 _ => (value.as_str(), 10),
295 };
296 i64::from_str_radix(digits, radix)
297 }
298
299 pub(crate) fn parse_float(value: &str) -> std::result::Result<f64, std::num::ParseFloatError> {
300 value.replace('_', "").parse()
301 }
302
303 pub fn as_bool(&self) -> Option<bool> {
304 match self {
305 Value::Bool(b) => Some(*b),
306 _ => None,
307 }
308 }
309
310 pub fn as_integer(&self) -> Option<i64> {
311 match self {
312 Value::Integer(n) => Some(*n),
313 _ => None,
314 }
315 }
316
317 pub fn as_float(&self) -> Option<f64> {
318 match self {
319 Value::Float(f) => Some(*f),
320 _ => None,
321 }
322 }
323
324 pub fn as_string(&self) -> Option<&str> {
325 match self {
326 Value::String(s) => Some(s),
327 _ => None,
328 }
329 }
330
331 pub fn as_bytes(&self) -> Option<&[u8]> {
332 match self {
333 Value::Bytes(bytes) => Some(bytes),
334 _ => None,
335 }
336 }
337
338 #[cfg(feature = "temporal")]
339 pub fn as_datetime(&self) -> Option<&chrono::DateTime<chrono::FixedOffset>> {
340 match self {
341 Value::DateTime(value) => Some(&value.value),
342 _ => None,
343 }
344 }
345
346 #[cfg(feature = "temporal")]
347 pub fn as_duration(&self) -> Option<i64> {
348 match self {
349 Value::Duration(value) => Some(*value),
350 _ => None,
351 }
352 }
353
354 pub fn as_array(&self) -> Option<&[Value]> {
355 match self {
356 Value::Array(a) => Some(a),
357 _ => None,
358 }
359 }
360
361 pub fn as_map(&self) -> Option<&IndexMap<String, Value>> {
362 match self {
363 Value::Map(m) => Some(m),
364 _ => None,
365 }
366 }
367
368 pub fn as_keyed_map(&self) -> Option<&[(Value, Value)]> {
369 match self {
370 Value::KeyedMap(m) => Some(m),
371 _ => None,
372 }
373 }
374
375 pub fn is_nil(&self) -> bool {
376 matches!(self, Value::Nil)
377 }
378}
379
380impl<K, V> FromIterator<(K, V)> for Value
381where
382 K: Into<String>,
383 V: Into<Value>,
384{
385 fn from_iter<I>(iter: I) -> Self
386 where I: IntoIterator<Item = (K, V)> {
387 Value::Map(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
388 }
389}
390
391impl AsRef<Value> for Value {
392 fn as_ref(&self) -> &Value {
393 self
394 }
395}
396
397impl From<i64> for Value {
398 fn from(n: i64) -> Self {
399 Value::Integer(n)
400 }
401}
402
403impl From<i32> for Value {
404 fn from(n: i32) -> Self {
405 Value::Integer(n as i64)
406 }
407}
408
409impl From<usize> for Value {
410 fn from(n: usize) -> Self {
411 Value::Integer(n as i64)
412 }
413}
414
415impl From<f64> for Value {
416 fn from(f: f64) -> Self {
417 Value::Float(f)
418 }
419}
420
421impl From<bool> for Value {
422 fn from(b: bool) -> Self {
423 Value::Bool(b)
424 }
425}
426
427impl From<String> for Value {
428 fn from(s: String) -> Self {
429 Value::String(s)
430 }
431}
432
433impl From<&String> for Value {
434 fn from(s: &String) -> Self {
435 s.to_string().into()
436 }
437}
438
439impl From<&str> for Value {
440 fn from(s: &str) -> Self {
441 s.to_string().into()
442 }
443}
444
445impl<V: Into<Value>> From<Vec<V>> for Value {
446 fn from(a: Vec<V>) -> Self {
447 Value::Array(a.into_iter().map(|v| v.into()).collect())
448 }
449}
450
451impl From<IndexMap<String, Value>> for Value {
452 fn from(m: IndexMap<String, Value>) -> Self {
453 Value::Map(m)
454 }
455}
456
457impl Display for Value {
458 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
459 match self {
460 Value::Integer(n) => write!(f, "{n}"),
461 Value::Float(n) => write!(f, "{n}"),
462 Value::Bool(b) => write!(f, "{b}"),
463 Value::Nil => write!(f, "nil"),
464 Value::String(s) => write!(
465 f,
466 r#""{}""#,
467 s.replace("\\", "\\\\")
468 .replace("\n", "\\n")
469 .replace("\r", "\\r")
470 .replace("\t", "\\t")
471 .replace("\"", "\\\"")
472 ),
473 Value::Bytes(bytes) => write!(
474 f,
475 "[{}]",
476 bytes
477 .iter()
478 .map(u8::to_string)
479 .collect::<Vec<String>>()
480 .join(" ")
481 ),
482 #[cfg(feature = "temporal")]
483 Value::DateTime(value) => write!(f, "{}", value.to_rfc3339()),
484 #[cfg(feature = "temporal")]
485 Value::Duration(value) => write!(f, "{value}ns"),
486 #[cfg(feature = "temporal")]
487 Value::Timezone(value) => write!(f, "{value}"),
488 #[cfg(feature = "temporal")]
489 Value::Month(value) | Value::Weekday(value) => write!(f, "{value}"),
490 Value::Array(a) => write!(
491 f,
492 "[{}]",
493 a.iter()
494 .map(|v| v.to_string())
495 .collect::<Vec<String>>()
496 .join(", ")
497 ),
498 Value::Map(m) => write!(
499 f,
500 "{{{}}}",
501 m.iter()
502 .map(|(k, v)| format!("{}: {}", k, v))
503 .collect::<Vec<String>>()
504 .join(", ")
505 ),
506 Value::KeyedMap(m) => write!(
507 f,
508 "{{{}}}",
509 m.iter()
510 .map(|(k, v)| format!("{}: {}", k, v))
511 .collect::<Vec<String>>()
512 .join(", ")
513 ),
514 }
515 }
516}
517
518impl From<Pairs<'_, Rule>> for Value {
519 fn from(mut pairs: Pairs<Rule>) -> Self {
520 pairs.next().unwrap().into()
521 }
522}
523
524impl From<Pair<'_, Rule>> for Value {
525 fn from(pair: Pair<Rule>) -> Self {
526 trace!("{:?} = {}", pair.as_rule(), pair.as_str());
527 match pair.as_rule() {
528 Rule::value => pair.into_inner().into(),
529 Rule::nil => Value::Nil,
530 Rule::bool => Value::Bool(pair.as_str().parse().unwrap()),
531 Rule::int => {
532 Value::Integer(Value::parse_integer(pair.as_str()).expect("literal validated"))
533 }
534 Rule::decimal => {
535 Value::Float(Value::parse_float(pair.as_str()).expect("literal validated"))
536 }
537 Rule::bytes => Value::Bytes(parse_bytes_literal(pair.as_str())),
538 Rule::string_multiline => pair.into_inner().as_str().into(),
539 Rule::string => pair
540 .into_inner()
541 .as_str()
542 .replace("\\\\", "\\")
543 .replace("\\n", "\n")
544 .replace("\\r", "\r")
545 .replace("\\t", "\t")
546 .replace("\\\"", "\"")
547 .into(),
548 rule => unreachable!("Unexpected rule: {rule:?} {}", pair.as_str()),
560 }
561 }
562}
563
564fn parse_bytes_literal(literal: &str) -> Vec<u8> {
565 let mut chars = literal[2..literal.len() - 1].chars();
566 let mut bytes = Vec::new();
567 while let Some(character) = chars.next() {
568 if character != '\\' {
569 let mut encoded = [0; 4];
570 bytes.extend_from_slice(character.encode_utf8(&mut encoded).as_bytes());
571 continue;
572 }
573
574 let escape = chars.next().expect("byte escape validated by grammar");
575 match escape {
576 'a' => bytes.push(7),
577 'b' => bytes.push(8),
578 'f' => bytes.push(12),
579 'n' => bytes.push(b'\n'),
580 'r' => bytes.push(b'\r'),
581 't' => bytes.push(b'\t'),
582 'v' => bytes.push(11),
583 '\\' | '\'' | '"' => bytes.push(escape as u8),
584 'x' => {
585 let digits = [
586 chars.next().expect("hex escape validated by grammar"),
587 chars.next().expect("hex escape validated by grammar"),
588 ];
589 bytes.push(
590 u8::from_str_radix(&digits.iter().collect::<String>(), 16)
591 .expect("hex escape validated by grammar"),
592 );
593 }
594 digit @ '0'..='7' => {
595 let digits = [
596 digit,
597 chars.next().expect("octal escape validated by grammar"),
598 chars.next().expect("octal escape validated by grammar"),
599 ];
600 bytes.push(
601 u8::from_str_radix(&digits.iter().collect::<String>(), 8)
602 .expect("octal escape validated by grammar"),
603 );
604 }
605 _ => unreachable!("byte escape validated by grammar"),
606 }
607 }
608 bytes
609}