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    process_value, ProcessValue, ProcessingResult, ProcessingState, Processor, ValueType,
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_string()), meta),
728            Annotated(Some(Value::Bool(false)), meta) => Annotated(Some("False".to_string()), 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 ProcessValue for Timestamp {
821    #[inline]
822    fn value_type(&self) -> EnumSet<ValueType> {
823        EnumSet::only(ValueType::DateTime)
824    }
825
826    #[inline]
827    fn process_value<P>(
828        &mut self,
829        meta: &mut Meta,
830        processor: &mut P,
831        state: &ProcessingState<'_>,
832    ) -> ProcessingResult
833    where
834        P: Processor,
835    {
836        processor.process_timestamp(self, meta, state)
837    }
838}
839
840impl From<DateTime<Utc>> for Timestamp {
841    fn from(value: DateTime<Utc>) -> Self {
842        Timestamp(value)
843    }
844}
845
846impl Add<Duration> for Timestamp {
847    type Output = Self;
848
849    fn add(self, duration: Duration) -> Self::Output {
850        Timestamp(self.0 + duration)
851    }
852}
853
854impl Sub<Timestamp> for Timestamp {
855    type Output = chrono::Duration;
856
857    fn sub(self, rhs: Timestamp) -> Self::Output {
858        self.into_inner() - rhs.into_inner()
859    }
860}
861
862impl PartialEq<DateTime<Utc>> for Timestamp {
863    fn eq(&self, other: &DateTime<Utc>) -> bool {
864        &self.0 == other
865    }
866}
867
868impl PartialOrd<DateTime<Utc>> for Timestamp {
869    fn partial_cmp(&self, other: &DateTime<Utc>) -> Option<Ordering> {
870        self.0.partial_cmp(other)
871    }
872}
873
874impl fmt::Display for Timestamp {
875    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
876        self.0.fmt(f)
877    }
878}
879
880pub fn datetime_to_timestamp(dt: DateTime<Utc>) -> f64 {
881    // f64s cannot store nanoseconds. To verify this just try to fit the current timestamp in
882    // nanoseconds into a 52-bit number (which is the significand of a double).
883    //
884    // Round off to microseconds to not show more decimal points than we know are correct. Anything
885    // else might trick the user into thinking the nanoseconds in those timestamps mean anything.
886    //
887    // This needs to be done regardless of whether the input value was a ISO-formatted string or a
888    // number because it all ends up as a f64 on serialization.
889    //
890    // If we want to support nanoseconds at some point we will probably have to start using strings
891    // everywhere. Even then it's unclear how to deal with it in Python code as a `datetime` cannot
892    // store nanoseconds.
893    //
894    // We use `timestamp_subsec_nanos` instead of `timestamp_subsec_micros` anyway to get better
895    // rounding behavior.
896    let micros = (f64::from(dt.timestamp_subsec_nanos()) / 1_000f64).round();
897    dt.timestamp() as f64 + (micros / 1_000_000f64)
898}
899
900fn utc_result_to_annotated<V: IntoValue>(
901    result: LocalResult<DateTime<Utc>>,
902    original_value: V,
903    mut meta: Meta,
904) -> Annotated<DateTime<Utc>> {
905    match result {
906        LocalResult::Single(value) => Annotated(Some(value), meta),
907        LocalResult::Ambiguous(_, _) => {
908            meta.add_error(Error::expected("ambiguous timestamp"));
909            meta.set_original_value(Some(original_value));
910            Annotated(None, meta)
911        }
912        LocalResult::None => {
913            meta.add_error(Error::invalid("timestamp out of range"));
914            meta.set_original_value(Some(original_value));
915            Annotated(None, meta)
916        }
917    }
918}
919
920impl FromValue for Timestamp {
921    fn from_value(value: Annotated<Value>) -> Annotated<Self> {
922        let rv = match value {
923            Annotated(Some(Value::String(value)), mut meta) => {
924                // NaiveDateTime parses "%Y-%m-%dT%H:%M:%S%.f"
925                let parsed = match value.parse::<NaiveDateTime>() {
926                    Ok(dt) => Ok(DateTime::from_naive_utc_and_offset(dt, Utc)),
927
928                    // XXX: This actually accepts more than RFC 3339. SDKs are strongly discouraged
929                    // from exercising that freedom. We should only support RFC3339.
930                    //
931                    // See https://github.com/chronotope/chrono/pull/516 (docs PR) for what this
932                    // actually does: It appears to accept a superset of RFC3339, but only a subset
933                    // of ISO 8601.
934                    Err(_) => value.parse(),
935                };
936                match parsed {
937                    Ok(value) => Annotated(Some(value), meta),
938                    Err(err) => {
939                        meta.add_error(Error::invalid(err));
940                        meta.set_original_value(Some(value));
941                        Annotated(None, meta)
942                    }
943                }
944            }
945            Annotated(Some(Value::U64(ts)), meta) => {
946                utc_result_to_annotated(Utc.timestamp_opt(ts as i64, 0), ts, meta)
947            }
948            Annotated(Some(Value::I64(ts)), meta) => {
949                utc_result_to_annotated(Utc.timestamp_opt(ts, 0), ts, meta)
950            }
951            Annotated(Some(Value::F64(ts)), meta) => {
952                let secs = ts as i64;
953                // at this point we probably already lose nanosecond precision, but we deal with
954                // this in `datetime_to_timestamp`.
955                let nanos = (ts.fract() * 1_000_000_000f64) as u32;
956                utc_result_to_annotated(Utc.timestamp_opt(secs, nanos), ts, meta)
957            }
958            Annotated(None, meta) => Annotated(None, meta),
959            Annotated(Some(value), mut meta) => {
960                meta.add_error(Error::expected("a timestamp"));
961                meta.set_original_value(Some(value));
962                Annotated(None, meta)
963            }
964        };
965
966        match rv {
967            Annotated(Some(value), mut meta) => {
968                if value.year() > 9999 {
969                    // We need to enforce this because Python has a max value for year and
970                    // otherwise crashes. Also this is probably nicer UX than silently showing the
971                    // wrong value.
972                    meta.add_error(Error::invalid("timestamp out of range"));
973                    meta.set_original_value(Some(Timestamp(value)));
974                    Annotated(None, meta)
975                } else {
976                    Annotated(Some(Timestamp(value)), meta)
977                }
978            }
979            x => x.map_value(Timestamp),
980        }
981    }
982}
983
984impl IntoValue for Timestamp {
985    fn into_value(self) -> Value {
986        Value::F64(datetime_to_timestamp(self.0))
987    }
988
989    fn serialize_payload<S>(&self, s: S, _behavior: SkipSerialization) -> Result<S::Ok, S::Error>
990    where
991        Self: Sized,
992        S: Serializer,
993    {
994        Serialize::serialize(&datetime_to_timestamp(self.0), s)
995    }
996}
997
998impl Empty for Timestamp {
999    fn is_empty(&self) -> bool {
1000        false
1001    }
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006    use similar_asserts::assert_eq;
1007
1008    use super::*;
1009
1010    #[test]
1011    fn test_values_serialization() {
1012        let value = Annotated::new(Values {
1013            values: Annotated::new(vec![
1014                Annotated::new(0u64),
1015                Annotated::new(1u64),
1016                Annotated::new(2u64),
1017            ]),
1018            other: Object::default(),
1019        });
1020        assert_eq!(value.to_json().unwrap(), "{\"values\":[0,1,2]}");
1021    }
1022
1023    #[test]
1024    fn test_values_deserialization() {
1025        #[derive(Clone, Debug, Empty, FromValue, IntoValue, PartialEq)]
1026        struct Exception {
1027            #[metastructure(field = "type")]
1028            ty: Annotated<String>,
1029            value: Annotated<String>,
1030        }
1031        let value = Annotated::<Values<Exception>>::from_json(
1032            r#"{"values": [{"type": "Test", "value": "aha!"}]}"#,
1033        )
1034        .unwrap();
1035        assert_eq!(
1036            value,
1037            Annotated::new(Values::new(vec![Annotated::new(Exception {
1038                ty: Annotated::new("Test".to_string()),
1039                value: Annotated::new("aha!".to_string()),
1040            })]))
1041        );
1042
1043        let value =
1044            Annotated::<Values<Exception>>::from_json(r#"[{"type": "Test", "value": "aha!"}]"#)
1045                .unwrap();
1046        assert_eq!(
1047            value,
1048            Annotated::new(Values::new(vec![Annotated::new(Exception {
1049                ty: Annotated::new("Test".to_string()),
1050                value: Annotated::new("aha!".to_string()),
1051            })]))
1052        );
1053
1054        let value =
1055            Annotated::<Values<Exception>>::from_json(r#"{"type": "Test", "value": "aha!"}"#)
1056                .unwrap();
1057        assert_eq!(
1058            value,
1059            Annotated::new(Values::new(vec![Annotated::new(Exception {
1060                ty: Annotated::new("Test".to_string()),
1061                value: Annotated::new("aha!".to_string()),
1062            })]))
1063        );
1064    }
1065
1066    #[test]
1067    fn test_hex_to_string() {
1068        assert_eq!("0x0", &Addr(0).to_string());
1069        assert_eq!("0x2a", &Addr(42).to_string());
1070    }
1071
1072    #[test]
1073    fn test_hex_from_string() {
1074        assert_eq!(Addr(0), "0".parse().unwrap());
1075        assert_eq!(Addr(42), "42".parse().unwrap());
1076        assert_eq!(Addr(42), "0x2a".parse().unwrap());
1077        assert_eq!(Addr(42), "0X2A".parse().unwrap());
1078    }
1079
1080    #[test]
1081    fn test_hex_serialization() {
1082        let value = Value::String("0x2a".to_string());
1083        let addr: Annotated<Addr> = FromValue::from_value(Annotated::new(value));
1084        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1085        let value = Value::U64(42);
1086        let addr: Annotated<Addr> = FromValue::from_value(Annotated::new(value));
1087        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1088    }
1089
1090    #[test]
1091    fn test_hex_deserialization() {
1092        let addr = Annotated::<Addr>::from_json("\"0x2a\"").unwrap();
1093        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1094        let addr = Annotated::<Addr>::from_json("42").unwrap();
1095        assert_eq!(addr.payload_to_json().unwrap(), "\"0x2a\"");
1096    }
1097
1098    #[test]
1099    fn test_level() {
1100        assert_eq!(
1101            Level::Info,
1102            Annotated::<Level>::from_json("\"log\"").unwrap().0.unwrap()
1103        );
1104        assert_eq!(
1105            Level::Warning,
1106            Annotated::<Level>::from_json("30").unwrap().0.unwrap()
1107        );
1108        assert_eq!(
1109            Level::Fatal,
1110            Annotated::<Level>::from_json("\"critical\"")
1111                .unwrap()
1112                .0
1113                .unwrap()
1114        );
1115    }
1116
1117    #[test]
1118    fn test_ip_addr() {
1119        assert_eq!(
1120            IpAddr("{{auto}}".into()),
1121            Annotated::<IpAddr>::from_json("\"{{auto}}\"")
1122                .unwrap()
1123                .0
1124                .unwrap()
1125        );
1126        assert_eq!(
1127            IpAddr("127.0.0.1".into()),
1128            Annotated::<IpAddr>::from_json("\"127.0.0.1\"")
1129                .unwrap()
1130                .0
1131                .unwrap()
1132        );
1133        assert_eq!(
1134            IpAddr("::1".into()),
1135            Annotated::<IpAddr>::from_json("\"::1\"")
1136                .unwrap()
1137                .0
1138                .unwrap()
1139        );
1140        assert_eq!(
1141            Annotated::from_error(
1142                Error::expected("an ip address"),
1143                Some(Value::String("clearly invalid value".into()))
1144            ),
1145            Annotated::<IpAddr>::from_json("\"clearly invalid value\"").unwrap()
1146        );
1147    }
1148
1149    #[test]
1150    fn test_timestamp_year_out_of_range() {
1151        #[derive(Debug, FromValue, Default, Empty, IntoValue)]
1152        struct Helper {
1153            foo: Annotated<Timestamp>,
1154        }
1155
1156        let x: Annotated<Helper> = Annotated::from_json(r#"{"foo": 1562770897893}"#).unwrap();
1157        assert_eq!(
1158            x.to_json_pretty().unwrap(),
1159            r#"{
1160  "foo": null,
1161  "_meta": {
1162    "foo": {
1163      "": {
1164        "err": [
1165          [
1166            "invalid_data",
1167            {
1168              "reason": "timestamp out of range"
1169            }
1170          ]
1171        ],
1172        "val": 1562770897893.0
1173      }
1174    }
1175  }
1176}"#
1177        );
1178    }
1179
1180    #[test]
1181    fn test_timestamp_completely_out_of_range() {
1182        #[derive(Debug, FromValue, Default, Empty, IntoValue)]
1183        struct Helper {
1184            foo: Annotated<Timestamp>,
1185        }
1186
1187        let x: Annotated<Helper> =
1188            Annotated::from_json(r#"{"foo": -10000000000000000.0}"#).unwrap();
1189        assert_eq!(
1190            x.to_json_pretty().unwrap(),
1191            r#"{
1192  "foo": null,
1193  "_meta": {
1194    "foo": {
1195      "": {
1196        "err": [
1197          [
1198            "invalid_data",
1199            {
1200              "reason": "timestamp out of range"
1201            }
1202          ]
1203        ],
1204        "val": -1e16
1205      }
1206    }
1207  }
1208}"#
1209        );
1210    }
1211}