relay_event_schema/protocol/
types.rs

1//! Common types of the protocol.
2use std::borrow::Cow;
3use std::cmp::Ordering;
4use std::ops::{Add, Sub};
5use std::str::FromStr;
6use std::{fmt, net};
7
8use chrono::{DateTime, Datelike, Duration, LocalResult, NaiveDateTime, TimeZone, Utc};
9use enumset::EnumSet;
10use relay_protocol::{
11    Annotated, Array, Empty, Error, ErrorKind, FromValue, IntoValue, Meta, Object,
12    SkipSerialization, Value,
13};
14use serde::{Deserialize, Deserializer, Serialize, Serializer};
15
16use crate::processor::{
17    ProcessValue, ProcessingResult, ProcessingState, Processor, ValueType, process_value,
18};
19
20/// An array-like wrapper used in various places.
21#[derive(Clone, Debug, PartialEq, Empty, IntoValue, ProcessValue)]
22#[metastructure(process_func = "process_values")]
23pub struct Values<T> {
24    /// The values of the collection.
25    #[metastructure(required = true, skip_serialization = "empty_deep")]
26    pub values: Annotated<Array<T>>,
27
28    /// Additional arbitrary fields for forwards compatibility.
29    #[metastructure(additional_properties)]
30    pub other: Object<Value>,
31}
32
33impl<T> Default for Values<T> {
34    fn default() -> Values<T> {
35        // Default implemented manually even if <T> does not impl Default.
36        Values::new(Vec::new())
37    }
38}
39
40impl<T> Values<T> {
41    /// Constructs a new value array from a given array
42    pub fn new(values: Array<T>) -> Values<T> {
43        Values {
44            values: Annotated::new(values),
45            other: Object::default(),
46        }
47    }
48}
49
50impl<T: FromValue> FromValue for Values<T> {
51    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
52        match value {
53            // Example:
54            // {"threads": [foo, bar, baz]}
55            Annotated(Some(Value::Array(items)), meta) => Annotated(
56                Some(Values {
57                    values: Annotated(
58                        Some(items.into_iter().map(FromValue::from_value).collect()),
59                        meta,
60                    ),
61                    other: Object::new(),
62                }),
63                Meta::default(),
64            ),
65            Annotated(Some(Value::Object(mut obj)), meta) => {
66                if obj.is_empty() {
67                    // Example:
68                    // {"exception": {}}
69                    // {"threads": {}}
70                    Annotated(None, meta)
71                } else if let Some(values) = obj.remove("values") {
72                    // Example:
73                    // {"exception": {"values": [foo, bar, baz]}}
74                    Annotated(
75                        Some(Values {
76                            values: FromValue::from_value(values),
77                            other: obj,
78                        }),
79                        meta,
80                    )
81                } else {
82                    // Example:
83                    // {"exception": {"type": "ZeroDivisonError"}
84                    Annotated(
85                        Some(Values {
86                            values: Annotated(
87                                Some(vec![FromValue::from_value(Annotated(
88                                    Some(Value::Object(obj)),
89                                    meta,
90                                ))]),
91                                Meta::default(),
92                            ),
93                            other: Object::new(),
94                        }),
95                        Meta::default(),
96                    )
97                }
98            }
99            Annotated(None, meta) => Annotated(None, meta),
100            Annotated(Some(value), mut meta) => {
101                meta.add_error(Error::expected("a list or values object"));
102                meta.set_original_value(Some(value));
103                Annotated(None, meta)
104            }
105        }
106    }
107}
108
109/// A trait to abstract over pairs.
110pub trait AsPair {
111    type Key: AsRef<str>;
112    type Value: ProcessValue;
113
114    /// Constructs this value from a raw tuple.
115    fn from_pair(pair: (Annotated<Self::Key>, Annotated<Self::Value>)) -> Self;
116
117    /// Converts this pair into a raw tuple.
118    fn into_pair(self) -> (Annotated<Self::Key>, Annotated<Self::Value>);
119
120    /// Extracts a key and value pair from the object.
121    fn as_pair(&self) -> (&Annotated<Self::Key>, &Annotated<Self::Value>);
122
123    /// Extracts the mutable key and value pair from the object.
124    fn as_pair_mut(&mut self) -> (&mut Annotated<Self::Key>, &mut Annotated<Self::Value>);
125
126    /// Returns a reference to the string representation of the key.
127    fn key(&self) -> Option<&str> {
128        self.as_pair().0.as_str()
129    }
130
131    /// Returns a reference to the value.
132    fn value(&self) -> Option<&Self::Value> {
133        self.as_pair().1.value()
134    }
135}
136
137impl<K, V> AsPair for (Annotated<K>, Annotated<V>)
138where
139    K: AsRef<str>,
140    V: ProcessValue,
141{
142    type Key = K;
143    type Value = V;
144
145    fn from_pair(pair: (Annotated<Self::Key>, Annotated<Self::Value>)) -> Self {
146        pair
147    }
148
149    fn into_pair(self) -> (Annotated<Self::Key>, Annotated<Self::Value>) {
150        self
151    }
152
153    fn as_pair(&self) -> (&Annotated<Self::Key>, &Annotated<Self::Value>) {
154        (&self.0, &self.1)
155    }
156
157    fn as_pair_mut(&mut self) -> (&mut Annotated<Self::Key>, &mut Annotated<Self::Value>) {
158        (&mut self.0, &mut self.1)
159    }
160}
161
162/// A mixture of a hashmap and an array.
163#[derive(Clone, Debug, Default, PartialEq, Empty, IntoValue)]
164pub struct PairList<T>(pub Array<T>);
165
166impl<T, K, V> PairList<T>
167where
168    K: AsRef<str>,
169    V: ProcessValue,
170    T: AsPair<Key = K, Value = V>,
171{
172    /// Searches for an entry with the given key and returns its position.
173    pub fn position<Q>(&self, key: Q) -> Option<usize>
174    where
175        Q: AsRef<str>,
176    {
177        let key = key.as_ref();
178        self.0
179            .iter()
180            .filter_map(Annotated::value)
181            .position(|entry| entry.as_pair().0.as_str() == Some(key))
182    }
183
184    /// Returns a reference to the annotated value corresponding to the key.
185    ///
186    /// The key may be any borrowed form of the pairlist's key type. If there are multiple entries
187    /// with the same key, the first is returned.
188    pub fn get<'a, Q>(&'a self, key: Q) -> Option<&'a Annotated<V>>
189    where
190        Q: AsRef<str>,
191        K: 'a,
192    {
193        self.position(key)
194            .and_then(|pos| self.0.get(pos))
195            .and_then(Annotated::value)
196            .map(|pair| pair.as_pair().1)
197    }
198
199    /// Returns a reference to the value corresponding to the key.
200    ///
201    /// The key may be any borrowed form of the pairlist's key type. If there are multiple entries
202    /// with the same key, the first is returned.
203    pub fn get_value<'a, Q>(&'a self, key: Q) -> Option<&'a V>
204    where
205        Q: AsRef<str>,
206        K: 'a,
207    {
208        self.get(key).and_then(Annotated::value)
209    }
210
211    /// Returns `true` if the pair list contains a value for the specified key.
212    pub fn contains<Q>(&self, key: Q) -> bool
213    where
214        Q: AsRef<str>,
215    {
216        self.position(key).is_some()
217    }
218
219    /// Removes an entry matching the given key and returns its value, if found.
220    pub fn remove<Q>(&mut self, key: Q) -> Option<Annotated<V>>
221    where
222        Q: AsRef<str>,
223    {
224        self.position(key)
225            .and_then(|index| self.0.remove(index).0)
226            .map(|entry| entry.into_pair().1)
227    }
228
229    /// Inserts a value into the list and returns the old value.
230    pub fn insert(&mut self, key: K, value: Annotated<V>) -> Option<Annotated<V>> {
231        match self.position(key.as_ref()) {
232            Some(index) => self
233                .get_mut(index)
234                .and_then(|annotated| annotated.value_mut().as_mut())
235                .map(|pair| std::mem::replace(pair.as_pair_mut().1, value)),
236            None => {
237                self.push(Annotated::new(T::from_pair((Annotated::new(key), value))));
238                None
239            }
240        }
241    }
242}
243
244impl<T> std::ops::Deref for PairList<T> {
245    type Target = Array<T>;
246
247    fn deref(&self) -> &Self::Target {
248        &self.0
249    }
250}
251
252impl<T> std::ops::DerefMut for PairList<T> {
253    fn deref_mut(&mut self) -> &mut Self::Target {
254        &mut self.0
255    }
256}
257
258impl<A> FromIterator<Annotated<A>> for PairList<A> {
259    fn from_iter<T>(iter: T) -> Self
260    where
261        T: IntoIterator<Item = Annotated<A>>,
262    {
263        PairList(FromIterator::from_iter(iter))
264    }
265}
266
267impl<T> From<Array<T>> for PairList<T> {
268    fn from(value: Array<T>) -> Self {
269        PairList(value)
270    }
271}
272
273impl<T: FromValue> FromValue for PairList<T> {
274    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
275        match value {
276            Annotated(Some(Value::Array(items)), meta) => {
277                let mut rv = Vec::new();
278                for item in items.into_iter() {
279                    rv.push(T::from_value(item));
280                }
281                Annotated(Some(PairList(rv)), meta)
282            }
283            Annotated(Some(Value::Object(items)), meta) => {
284                let mut rv = Vec::new();
285                for (key, value) in items.into_iter() {
286                    rv.push(T::from_value(Annotated::new(Value::Array(vec![
287                        Annotated::new(Value::String(key)),
288                        value,
289                    ]))));
290                }
291                Annotated(Some(PairList(rv)), meta)
292            }
293            other => FromValue::from_value(other).map_value(PairList),
294        }
295    }
296}
297
298impl<T> ProcessValue for PairList<T>
299where
300    T: ProcessValue + AsPair,
301{
302    #[inline]
303    fn value_type(&self) -> EnumSet<ValueType> {
304        EnumSet::only(ValueType::Object)
305    }
306
307    #[inline]
308    fn process_value<P>(
309        &mut self,
310        meta: &mut Meta,
311        processor: &mut P,
312        state: &ProcessingState<'_>,
313    ) -> ProcessingResult
314    where
315        P: Processor,
316    {
317        processor.process_pairlist(self, meta, state)
318    }
319
320    fn process_child_values<P>(
321        &mut self,
322        processor: &mut P,
323        state: &ProcessingState<'_>,
324    ) -> ProcessingResult
325    where
326        P: Processor,
327    {
328        for (idx, pair) in self.0.iter_mut().enumerate() {
329            let state = state.enter_index(idx, state.inner_attrs(), ValueType::for_field(pair));
330            process_value(pair, processor, &state)?;
331        }
332
333        Ok(())
334    }
335}
336
337macro_rules! hex_metrastructure {
338    ($type:ident, $expectation:expr) => {
339        impl FromStr for $type {
340            type Err = std::num::ParseIntError;
341
342            fn from_str(s: &str) -> Result<$type, Self::Err> {
343                if s.starts_with("0x") || s.starts_with("0X") {
344                    u64::from_str_radix(&s[2..], 16).map($type)
345                } else {
346                    s.parse().map($type)
347                }
348            }
349        }
350
351        impl fmt::Display for $type {
352            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353                write!(f, "{:#x}", self.0)
354            }
355        }
356
357        impl Empty for $type {
358            #[inline]
359            fn is_empty(&self) -> bool {
360                false
361            }
362        }
363
364        impl FromValue for $type {
365            fn from_value(value: Annotated<Value>) -> Annotated<Self> {
366                match value {
367                    Annotated(Some(Value::String(value)), mut meta) => match value.parse() {
368                        Ok(value) => Annotated(Some(value), meta),
369                        Err(err) => {
370                            meta.add_error(Error::invalid(err));
371                            meta.set_original_value(Some(value));
372                            Annotated(None, meta)
373                        }
374                    },
375                    Annotated(Some(Value::U64(value)), meta) => Annotated(Some($type(value)), meta),
376                    Annotated(Some(Value::I64(value)), meta) => {
377                        Annotated(Some($type(value as u64)), meta)
378                    }
379                    Annotated(None, meta) => Annotated(None, meta),
380                    Annotated(Some(value), mut meta) => {
381                        meta.add_error(Error::expected($expectation));
382                        meta.set_original_value(Some(value));
383                        Annotated(None, meta)
384                    }
385                }
386            }
387        }
388
389        impl IntoValue for $type {
390            fn into_value(self) -> Value {
391                Value::String(self.to_string())
392            }
393            fn serialize_payload<S>(
394                &self,
395                s: S,
396                _behavior: relay_protocol::SkipSerialization,
397            ) -> Result<S::Ok, S::Error>
398            where
399                Self: Sized,
400                S: Serializer,
401            {
402                Serializer::collect_str(s, self)
403            }
404        }
405
406        impl ProcessValue for $type {}
407    };
408}
409
410/// Raised if a register value can't be parsed.
411#[derive(Debug)]
412pub struct InvalidRegVal;
413
414impl fmt::Display for InvalidRegVal {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        write!(f, "invalid register value")
417    }
418}
419
420impl std::error::Error for InvalidRegVal {}
421
422/// A register value.
423#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
424pub struct RegVal(pub u64);
425
426hex_metrastructure!(RegVal, "register value");
427
428/// An address
429#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
430pub struct Addr(pub u64);
431
432hex_metrastructure!(Addr, "address");
433relay_common::impl_str_serde!(Addr, "an address");
434
435/// An ip address.
436#[derive(
437    Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Empty, IntoValue, ProcessValue, Serialize,
438)]
439pub struct IpAddr(pub String);
440
441impl IpAddr {
442    /// Returns the auto marker ip address.
443    pub fn auto() -> IpAddr {
444        IpAddr("{{auto}}".into())
445    }
446
447    /// Parses an `IpAddr` from a string
448    pub fn parse<S>(value: S) -> Result<Self, S>
449    where
450        S: AsRef<str> + Into<String>,
451    {
452        if value.as_ref() == "{{auto}}" {
453            return Ok(IpAddr(value.into()));
454        }
455
456        match net::IpAddr::from_str(value.as_ref()) {
457            Ok(_) => Ok(IpAddr(value.into())),
458            Err(_) => Err(value),
459        }
460    }
461
462    /// Checks if the ip address is set to the auto marker.
463    pub fn is_auto(&self) -> bool {
464        self.0 == "{{auto}}"
465    }
466
467    /// Checks whether the contained ip address is still valid (relevant for PII processing).
468    pub fn is_valid(&self) -> bool {
469        self.is_auto() || net::IpAddr::from_str(&self.0).is_ok()
470    }
471
472    /// Returns the string value of this ip address.
473    pub fn as_str(&self) -> &str {
474        &self.0
475    }
476
477    /// Convert IP address into owned string.
478    pub fn into_inner(self) -> String {
479        self.0
480    }
481}
482
483impl AsRef<str> for IpAddr {
484    fn as_ref(&self) -> &str {
485        &self.0
486    }
487}
488
489impl Default for IpAddr {
490    fn default() -> Self {
491        IpAddr::auto()
492    }
493}
494
495impl From<std::net::IpAddr> for IpAddr {
496    fn from(ip_addr: std::net::IpAddr) -> Self {
497        Self(ip_addr.to_string())
498    }
499}
500
501impl fmt::Display for IpAddr {
502    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
503        self.0.fmt(f)
504    }
505}
506
507impl<'de> Deserialize<'de> for IpAddr {
508    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
509    where
510        D: Deserializer<'de>,
511    {
512        let string = Cow::<'_, str>::deserialize(deserializer)?;
513        IpAddr::parse(string).map_err(|_| serde::de::Error::custom("expected an ip address"))
514    }
515}
516
517impl FromValue for IpAddr {
518    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
519        match value {
520            Annotated(Some(Value::String(value)), mut meta) => match IpAddr::parse(value) {
521                Ok(addr) => Annotated(Some(addr), meta),
522                Err(value) => {
523                    meta.add_error(Error::expected("an ip address"));
524                    meta.set_original_value(Some(value));
525                    Annotated(None, meta)
526                }
527            },
528            Annotated(None, meta) => Annotated(None, meta),
529            Annotated(Some(value), mut meta) => {
530                meta.add_error(Error::expected("an ip address"));
531                meta.set_original_value(Some(value));
532                Annotated(None, meta)
533            }
534        }
535    }
536}
537
538impl FromStr for IpAddr {
539    type Err = ();
540
541    fn from_str(value: &str) -> Result<Self, Self::Err> {
542        IpAddr::parse(value).map_err(|_| ())
543    }
544}
545
546/// An error used when parsing `Level`.
547#[derive(Debug)]
548pub struct ParseLevelError;
549
550impl fmt::Display for ParseLevelError {
551    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
552        write!(f, "invalid level")
553    }
554}
555
556impl std::error::Error for ParseLevelError {}
557
558/// Severity level of an event or breadcrumb.
559#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
560pub enum Level {
561    /// Indicates very spammy debug information.
562    Debug,
563    /// Informational messages.
564    #[default]
565    Info,
566    /// A warning.
567    Warning,
568    /// An error.
569    Error,
570    /// Similar to error but indicates a critical event that usually causes a shutdown.
571    Fatal,
572}
573
574impl Level {
575    pub fn name(self) -> &'static str {
576        match self {
577            Level::Debug => "debug",
578            Level::Info => "info",
579            Level::Warning => "warning",
580            Level::Error => "error",
581            Level::Fatal => "fatal",
582        }
583    }
584
585    fn from_python_level(value: u64) -> Option<Level> {
586        Some(match value {
587            10 => Level::Debug,
588            20 => Level::Info,
589            30 => Level::Warning,
590            40 => Level::Error,
591            50 => Level::Fatal,
592            _ => return None,
593        })
594    }
595}
596
597impl FromStr for Level {
598    type Err = ParseLevelError;
599
600    fn from_str(string: &str) -> Result<Self, Self::Err> {
601        Ok(match string {
602            "debug" => Level::Debug,
603            "info" | "log" => Level::Info,
604            "warning" => Level::Warning,
605            "error" => Level::Error,
606            "fatal" | "critical" => Level::Fatal,
607            _ => return Err(ParseLevelError),
608        })
609    }
610}
611
612impl fmt::Display for Level {
613    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614        f.write_str(self.name())
615    }
616}
617
618impl FromValue for Level {
619    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
620        match value {
621            Annotated(Some(Value::String(value)), mut meta) => match value.parse() {
622                Ok(value) => Annotated(Some(value), meta),
623                Err(err) => {
624                    meta.add_error(Error::invalid(err));
625                    meta.set_original_value(Some(value));
626                    Annotated(None, meta)
627                }
628            },
629            Annotated(Some(Value::U64(val)), mut meta) => match Level::from_python_level(val) {
630                Some(value) => Annotated(Some(value), meta),
631                None => {
632                    meta.add_error(ErrorKind::InvalidData);
633                    meta.set_original_value(Some(val));
634                    Annotated(None, meta)
635                }
636            },
637            Annotated(Some(Value::I64(val)), mut meta) => {
638                match Level::from_python_level(val as u64) {
639                    Some(value) => Annotated(Some(value), meta),
640                    None => {
641                        meta.add_error(ErrorKind::InvalidData);
642                        meta.set_original_value(Some(val));
643                        Annotated(None, meta)
644                    }
645                }
646            }
647            Annotated(None, meta) => Annotated(None, meta),
648            Annotated(Some(value), mut meta) => {
649                meta.add_error(Error::expected("a level"));
650                meta.set_original_value(Some(value));
651                Annotated(None, meta)
652            }
653        }
654    }
655}
656
657impl IntoValue for Level {
658    fn into_value(self) -> Value {
659        Value::String(self.to_string())
660    }
661
662    fn serialize_payload<S>(&self, s: S, _behavior: SkipSerialization) -> Result<S::Ok, S::Error>
663    where
664        Self: Sized,
665        S: Serializer,
666    {
667        Serialize::serialize(self.name(), s)
668    }
669}
670
671impl ProcessValue for Level {}
672
673impl Empty for Level {
674    #[inline]
675    fn is_empty(&self) -> bool {
676        false
677    }
678}
679
680/// A "into-string" type of value. Emulates an invocation of `str(x)` in Python
681#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Empty, IntoValue, ProcessValue)]
682pub struct LenientString(pub String);
683
684impl LenientString {
685    /// Returns the string value.
686    pub fn as_str(&self) -> &str {
687        &self.0
688    }
689
690    /// Unwraps the inner raw string.
691    pub fn into_inner(self) -> String {
692        self.0
693    }
694}
695
696impl AsRef<str> for LenientString {
697    fn as_ref(&self) -> &str {
698        &self.0
699    }
700}
701
702impl std::ops::Deref for LenientString {
703    type Target = String;
704
705    fn deref(&self) -> &Self::Target {
706        &self.0
707    }
708}
709
710impl std::ops::DerefMut for LenientString {
711    fn deref_mut(&mut self) -> &mut Self::Target {
712        &mut self.0
713    }
714}
715
716impl From<String> for LenientString {
717    fn from(value: String) -> Self {
718        LenientString(value)
719    }
720}
721
722impl FromValue for LenientString {
723    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
724        match value {
725            Annotated(Some(Value::String(string)), meta) => Annotated(Some(string), meta),
726            // XXX: True/False instead of true/false because of old python code
727            Annotated(Some(Value::Bool(true)), meta) => Annotated(Some("True".to_owned()), meta),
728            Annotated(Some(Value::Bool(false)), meta) => Annotated(Some("False".to_owned()), meta),
729            Annotated(Some(Value::U64(num)), meta) => Annotated(Some(num.to_string()), meta),
730            Annotated(Some(Value::I64(num)), meta) => Annotated(Some(num.to_string()), meta),
731            Annotated(Some(Value::F64(num)), mut meta) => {
732                if num.abs() < (1i64 << 53) as f64 {
733                    Annotated(Some(num.trunc().to_string()), meta)
734                } else {
735                    meta.add_error(Error::expected("a number with JSON precision"));
736                    meta.set_original_value(Some(num));
737                    Annotated(None, meta)
738                }
739            }
740            Annotated(None, meta) => Annotated(None, meta),
741            Annotated(Some(value), mut meta) => {
742                meta.add_error(Error::expected("a primitive value"));
743                meta.set_original_value(Some(value));
744                Annotated(None, meta)
745            }
746        }
747        .map_value(LenientString)
748    }
749}
750
751/// A "into-string" type of value. All non-string values are serialized as JSON.
752#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Empty, IntoValue, ProcessValue)]
753pub struct JsonLenientString(pub String);
754
755impl JsonLenientString {
756    /// Returns the string value.
757    pub fn as_str(&self) -> &str {
758        &self.0
759    }
760
761    /// Unwraps the inner raw string.
762    pub fn into_inner(self) -> String {
763        self.0
764    }
765}
766
767impl AsRef<str> for JsonLenientString {
768    fn as_ref(&self) -> &str {
769        &self.0
770    }
771}
772
773impl std::ops::Deref for JsonLenientString {
774    type Target = String;
775
776    fn deref(&self) -> &Self::Target {
777        &self.0
778    }
779}
780
781impl std::ops::DerefMut for JsonLenientString {
782    fn deref_mut(&mut self) -> &mut Self::Target {
783        &mut self.0
784    }
785}
786
787impl From<JsonLenientString> for String {
788    fn from(value: JsonLenientString) -> Self {
789        value.0
790    }
791}
792
793impl FromValue for JsonLenientString {
794    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
795        match value {
796            Annotated(Some(Value::String(string)), meta) => Annotated(Some(string.into()), meta),
797            Annotated(Some(other), meta) => {
798                Annotated(Some(serde_json::to_string(&other).unwrap().into()), meta)
799            }
800            Annotated(None, meta) => Annotated(None, meta),
801        }
802    }
803}
804
805impl From<String> for JsonLenientString {
806    fn from(value: String) -> Self {
807        JsonLenientString(value)
808    }
809}
810
811#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
812pub struct Timestamp(pub DateTime<Utc>);
813
814impl Timestamp {
815    pub fn into_inner(self) -> DateTime<Utc> {
816        self.0
817    }
818}
819
820impl From<Timestamp> for uuid::Timestamp {
821    fn from(value: Timestamp) -> Self {
822        uuid::Timestamp::from_unix(
823            uuid::NoContext,
824            u64::try_from(value.0.timestamp()).unwrap_or(0),
825            value.0.timestamp_subsec_nanos(),
826        )
827    }
828}
829
830impl ProcessValue for Timestamp {
831    #[inline]
832    fn value_type(&self) -> EnumSet<ValueType> {
833        EnumSet::only(ValueType::DateTime)
834    }
835
836    #[inline]
837    fn process_value<P>(
838        &mut self,
839        meta: &mut Meta,
840        processor: &mut P,
841        state: &ProcessingState<'_>,
842    ) -> ProcessingResult
843    where
844        P: Processor,
845    {
846        processor.process_timestamp(self, meta, state)
847    }
848}
849
850impl From<DateTime<Utc>> for Timestamp {
851    fn from(value: DateTime<Utc>) -> Self {
852        Timestamp(value)
853    }
854}
855
856impl Add<Duration> for Timestamp {
857    type Output = Self;
858
859    fn add(self, duration: Duration) -> Self::Output {
860        Timestamp(self.0 + duration)
861    }
862}
863
864impl Sub<Timestamp> for Timestamp {
865    type Output = chrono::Duration;
866
867    fn sub(self, rhs: Timestamp) -> Self::Output {
868        self.into_inner() - rhs.into_inner()
869    }
870}
871
872impl PartialEq<DateTime<Utc>> for Timestamp {
873    fn eq(&self, other: &DateTime<Utc>) -> bool {
874        &self.0 == other
875    }
876}
877
878impl PartialOrd<DateTime<Utc>> for Timestamp {
879    fn partial_cmp(&self, other: &DateTime<Utc>) -> Option<Ordering> {
880        self.0.partial_cmp(other)
881    }
882}
883
884impl fmt::Display for Timestamp {
885    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
886        self.0.fmt(f)
887    }
888}
889
890/// Converts a [`DateTime`] to a `f64`, dealing with sub-microsecond float inaccuracies.
891///
892/// f64s cannot store nanoseconds. To verify this just try to fit the current timestamp in
893/// nanoseconds into a 52-bit number (which is the significand of a double).
894///
895/// Round off to microseconds to not show more decimal points than we know are correct. Anything
896/// else might trick the user into thinking the nanoseconds in those timestamps mean anything.
897///
898/// This needs to be done regardless of whether the input value was a ISO-formatted string or a
899/// number because it all ends up as a f64 on serialization.
900///
901/// If we want to support nanoseconds at some point we will probably have to start using strings
902/// everywhere. Even then it's unclear how to deal with it in Python code as a `datetime` cannot
903/// store nanoseconds.
904///
905/// See also: [`timestamp_to_datetime`].
906pub fn datetime_to_timestamp(dt: DateTime<Utc>) -> f64 {
907    // We use `timestamp_subsec_nanos` instead of `timestamp_subsec_micros` anyway to get better
908    // rounding behavior.
909    let micros = (f64::from(dt.timestamp_subsec_nanos()) / 1_000f64).round();
910    dt.timestamp() as f64 + (micros / 1_000_000f64)
911}
912
913/// Converts a `f64` Unix timestamp to a [`DateTime`], dealing with sub-microsecond float
914/// inaccuracies.
915///
916/// See also: [`datetime_to_timestamp`].
917pub fn timestamp_to_datetime(ts: f64) -> LocalResult<DateTime<Utc>> {
918    // Always floor, this works correctly for negative numbers as well.
919    let secs = ts.floor();
920    // This is always going to be positive, because we floored the seconds.
921    let fract = ts - secs;
922    let micros = (fract * 1_000_000f64).round() as u32;
923    // Rounding may produce another full second, in which case we need to manually handle the extra
924    // second.
925    match micros == 1_000_000 {
926        true => Utc.timestamp_opt(secs as i64 + 1, 0),
927        false => Utc.timestamp_opt(secs as i64, micros * 1_000),
928    }
929}
930
931fn utc_result_to_annotated<V: IntoValue>(
932    result: LocalResult<DateTime<Utc>>,
933    original_value: V,
934    mut meta: Meta,
935) -> Annotated<DateTime<Utc>> {
936    match result {
937        LocalResult::Single(value) => Annotated(Some(value), meta),
938        LocalResult::Ambiguous(_, _) => {
939            meta.add_error(Error::expected("ambiguous timestamp"));
940            meta.set_original_value(Some(original_value));
941            Annotated(None, meta)
942        }
943        LocalResult::None => {
944            meta.add_error(Error::invalid("timestamp out of range"));
945            meta.set_original_value(Some(original_value));
946            Annotated(None, meta)
947        }
948    }
949}
950
951impl FromValue for Timestamp {
952    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
953        let rv = match value {
954            Annotated(Some(Value::String(value)), mut meta) => {
955                // NaiveDateTime parses "%Y-%m-%dT%H:%M:%S%.f"
956                let parsed = match value.parse::<NaiveDateTime>() {
957                    Ok(dt) => Ok(DateTime::from_naive_utc_and_offset(dt, Utc)),
958
959                    // XXX: This actually accepts more than RFC 3339. SDKs are strongly discouraged
960                    // from exercising that freedom. We should only support RFC3339.
961                    //
962                    // See https://github.com/chronotope/chrono/pull/516 (docs PR) for what this
963                    // actually does: It appears to accept a superset of RFC3339, but only a subset
964                    // of ISO 8601.
965                    Err(_) => value.parse(),
966                };
967                match parsed {
968                    Ok(value) => Annotated(Some(value), meta),
969                    Err(err) => {
970                        meta.add_error(Error::invalid(err));
971                        meta.set_original_value(Some(value));
972                        Annotated(None, meta)
973                    }
974                }
975            }
976            Annotated(Some(Value::U64(ts)), meta) => {
977                utc_result_to_annotated(Utc.timestamp_opt(ts as i64, 0), ts, meta)
978            }
979            Annotated(Some(Value::I64(ts)), meta) => {
980                utc_result_to_annotated(Utc.timestamp_opt(ts, 0), ts, meta)
981            }
982            Annotated(Some(Value::F64(ts)), meta) => {
983                utc_result_to_annotated(timestamp_to_datetime(ts), ts, meta)
984            }
985            Annotated(None, meta) => Annotated(None, meta),
986            Annotated(Some(value), mut meta) => {
987                meta.add_error(Error::expected("a timestamp"));
988                meta.set_original_value(Some(value));
989                Annotated(None, meta)
990            }
991        };
992
993        match rv {
994            Annotated(Some(value), mut meta) => {
995                if value.year() > 9999 {
996                    // We need to enforce this because Python has a max value for year and
997                    // otherwise crashes. Also this is probably nicer UX than silently showing the
998                    // wrong value.
999                    meta.add_error(Error::invalid("timestamp out of range"));
1000                    meta.set_original_value(Some(Timestamp(value)));
1001                    Annotated(None, meta)
1002                } else {
1003                    Annotated(Some(Timestamp(value)), meta)
1004                }
1005            }
1006            x => x.map_value(Timestamp),
1007        }
1008    }
1009}
1010
1011impl IntoValue for Timestamp {
1012    fn into_value(self) -> Value {
1013        Value::F64(datetime_to_timestamp(self.0))
1014    }
1015
1016    fn serialize_payload<S>(&self, s: S, _behavior: SkipSerialization) -> Result<S::Ok, S::Error>
1017    where
1018        Self: Sized,
1019        S: Serializer,
1020    {
1021        Serialize::serialize(&datetime_to_timestamp(self.0), s)
1022    }
1023}
1024
1025impl Empty for Timestamp {
1026    fn is_empty(&self) -> bool {
1027        false
1028    }
1029}
1030
1031#[cfg(test)]
1032mod tests {
1033    use similar_asserts::assert_eq;
1034
1035    use super::*;
1036
1037    #[test]
1038    fn test_timestamp_to_datetime() {
1039        assert_eq!(timestamp_to_datetime(0.), Utc.timestamp_opt(0, 0));
1040        assert_eq!(timestamp_to_datetime(1000.), Utc.timestamp_opt(1000, 0));
1041        assert_eq!(timestamp_to_datetime(-1000.), Utc.timestamp_opt(-1000, 0));
1042        assert_eq!(
1043            timestamp_to_datetime(1.234_567),
1044            Utc.timestamp_opt(1, 234_567_000)
1045        );
1046        assert_eq!(timestamp_to_datetime(2.999_999_51), Utc.timestamp_opt(3, 0));
1047        assert_eq!(
1048            timestamp_to_datetime(2.999_999_45),
1049            Utc.timestamp_opt(2, 999_999_000)
1050        );
1051        assert_eq!(
1052            timestamp_to_datetime(-0.000_001),
1053            Utc.timestamp_opt(-1, 999_999_000)
1054        );
1055        assert_eq!(
1056            timestamp_to_datetime(-3.000_000_49),
1057            Utc.timestamp_opt(-3, 0)
1058        );
1059        assert_eq!(
1060            timestamp_to_datetime(-3.000_000_51),
1061            Utc.timestamp_opt(-4, 999_999_000)
1062        );
1063    }
1064
1065    #[test]
1066    fn test_values_serialization() {
1067        let value = Annotated::new(Values {
1068            values: Annotated::new(vec![
1069                Annotated::new(0u64),
1070                Annotated::new(1u64),
1071                Annotated::new(2u64),
1072            ]),
1073            other: Object::default(),
1074        });
1075        assert_eq!(value.to_json().unwrap(), "{\"values\":[0,1,2]}");
1076    }
1077
1078    #[test]
1079    fn test_values_deserialization() {
1080        #[derive(Clone, Debug, Empty, FromValue, IntoValue, PartialEq)]
1081        struct Exception {
1082            #[metastructure(field = "type")]
1083            ty: Annotated<String>,
1084            value: Annotated<String>,
1085        }
1086        let value = Annotated::<Values<Exception>>::from_json(
1087            r#"{"values": [{"type": "Test", "value": "aha!"}]}"#,
1088        )
1089        .unwrap();
1090        assert_eq!(
1091            value,
1092            Annotated::new(Values::new(vec![Annotated::new(Exception {
1093                ty: Annotated::new("Test".to_owned()),
1094                value: Annotated::new("aha!".to_owned()),
1095            })]))
1096        );
1097
1098        let value =
1099            Annotated::<Values<Exception>>::from_json(r#"[{"type": "Test", "value": "aha!"}]"#)
1100                .unwrap();
1101        assert_eq!(
1102            value,
1103            Annotated::new(Values::new(vec![Annotated::new(Exception {
1104                ty: Annotated::new("Test".to_owned()),
1105                value: Annotated::new("aha!".to_owned()),
1106            })]))
1107        );
1108
1109        let value =
1110            Annotated::<Values<Exception>>::from_json(r#"{"type": "Test", "value": "aha!"}"#)
1111                .unwrap();
1112        assert_eq!(
1113            value,
1114            Annotated::new(Values::new(vec![Annotated::new(Exception {
1115                ty: Annotated::new("Test".to_owned()),
1116                value: Annotated::new("aha!".to_owned()),
1117            })]))
1118        );
1119    }
1120
1121    #[test]
1122    fn test_hex_to_string() {
1123        assert_eq!("0x0", &Addr(0).to_string());
1124        assert_eq!("0x2a", &Addr(42).to_string());
1125    }
1126
1127    #[test]
1128    fn test_hex_from_string() {
1129        assert_eq!(Addr(0), "0".parse().unwrap());
1130        assert_eq!(Addr(42), "42".parse().unwrap());
1131        assert_eq!(Addr(42), "0x2a".parse().unwrap());
1132        assert_eq!(Addr(42), "0X2A".parse().unwrap());
1133    }
1134
1135    #[test]
1136    fn test_hex_serialization() {
1137        let value = Value::String("0x2a".to_owned());
1138        let addr: Annotated<Addr> = FromValue::from_value(Annotated::new(value));
1139        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1140        let value = Value::U64(42);
1141        let addr: Annotated<Addr> = FromValue::from_value(Annotated::new(value));
1142        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1143    }
1144
1145    #[test]
1146    fn test_hex_deserialization() {
1147        let addr = Annotated::<Addr>::from_json("\"0x2a\"").unwrap();
1148        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1149        let addr = Annotated::<Addr>::from_json("42").unwrap();
1150        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1151    }
1152
1153    #[test]
1154    fn test_level() {
1155        assert_eq!(
1156            Level::Info,
1157            Annotated::<Level>::from_json("\"log\"").unwrap().0.unwrap()
1158        );
1159        assert_eq!(
1160            Level::Warning,
1161            Annotated::<Level>::from_json("30").unwrap().0.unwrap()
1162        );
1163        assert_eq!(
1164            Level::Fatal,
1165            Annotated::<Level>::from_json("\"critical\"")
1166                .unwrap()
1167                .0
1168                .unwrap()
1169        );
1170    }
1171
1172    #[test]
1173    fn test_ip_addr() {
1174        assert_eq!(
1175            IpAddr("{{auto}}".into()),
1176            Annotated::<IpAddr>::from_json("\"{{auto}}\"")
1177                .unwrap()
1178                .0
1179                .unwrap()
1180        );
1181        assert_eq!(
1182            IpAddr("127.0.0.1".into()),
1183            Annotated::<IpAddr>::from_json("\"127.0.0.1\"")
1184                .unwrap()
1185                .0
1186                .unwrap()
1187        );
1188        assert_eq!(
1189            IpAddr("::1".into()),
1190            Annotated::<IpAddr>::from_json("\"::1\"")
1191                .unwrap()
1192                .0
1193                .unwrap()
1194        );
1195        assert_eq!(
1196            Annotated::from_error(
1197                Error::expected("an ip address"),
1198                Some(Value::String("clearly invalid value".into()))
1199            ),
1200            Annotated::<IpAddr>::from_json("\"clearly invalid value\"").unwrap()
1201        );
1202    }
1203
1204    #[test]
1205    fn test_timestamp_year_out_of_range() {
1206        #[derive(Debug, FromValue, Default, Empty, IntoValue)]
1207        struct Helper {
1208            foo: Annotated<Timestamp>,
1209        }
1210
1211        let x: Annotated<Helper> = Annotated::from_json(r#"{"foo": 1562770897893}"#).unwrap();
1212        assert_eq!(
1213            x.to_json_pretty().unwrap(),
1214            r#"{
1215  "foo": null,
1216  "_meta": {
1217    "foo": {
1218      "": {
1219        "err": [
1220          [
1221            "invalid_data",
1222            {
1223              "reason": "timestamp out of range"
1224            }
1225          ]
1226        ],
1227        "val": 1562770897893.0
1228      }
1229    }
1230  }
1231}"#
1232        );
1233    }
1234
1235    #[test]
1236    fn test_timestamp_completely_out_of_range() {
1237        #[derive(Debug, FromValue, Default, Empty, IntoValue)]
1238        struct Helper {
1239            foo: Annotated<Timestamp>,
1240        }
1241
1242        let x: Annotated<Helper> =
1243            Annotated::from_json(r#"{"foo": -10000000000000000.0}"#).unwrap();
1244        assert_eq!(
1245            x.to_json_pretty().unwrap(),
1246            r#"{
1247  "foo": null,
1248  "_meta": {
1249    "foo": {
1250      "": {
1251        "err": [
1252          [
1253            "invalid_data",
1254            {
1255              "reason": "timestamp out of range"
1256            }
1257          ]
1258        ],
1259        "val": -1e16
1260      }
1261    }
1262  }
1263}"#
1264        );
1265    }
1266}