sentry_types/protocol/
v7.rs

1//! The current latest sentry protocol version.
2//!
3//! Most constructs in the protocol map directly to types here but some
4//! cleanup by renaming attributes has been applied.  The idea here is that
5//! a future sentry protocol will be a cleanup of the old one and is mapped
6//! to similar values on the rust side.
7
8use std::borrow::Cow;
9use std::cmp;
10use std::convert::TryFrom;
11use std::fmt;
12use std::iter::FromIterator;
13use std::net::{AddrParseError, IpAddr};
14use std::ops;
15use std::str;
16use std::time::SystemTime;
17
18use self::debugid::{CodeId, DebugId};
19use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
20use thiserror::Error;
21
22pub use url::Url;
23pub use uuid::Uuid;
24
25use crate::utils::{display_from_str_opt, ts_rfc3339_opt, ts_seconds_float};
26
27pub use super::attachment::*;
28pub use super::envelope::*;
29pub use super::monitor::*;
30pub use super::session::*;
31
32/// An arbitrary (JSON) value.
33pub mod value {
34    pub use serde_json::value::{from_value, to_value, Index, Map, Number, Value};
35}
36
37/// The internally used arbitrary data map type.
38pub mod map {
39    pub use std::collections::btree_map::{BTreeMap as Map, *};
40}
41
42/// Represents a debug ID.
43pub mod debugid {
44    pub use debugid::{BreakpadFormat, CodeId, DebugId, ParseDebugIdError};
45}
46
47/// An arbitrary (JSON) value.
48pub use self::value::Value;
49
50/// The internally used map type.
51pub use self::map::Map;
52
53/// A wrapper type for collections with attached meta data.
54///
55/// The JSON payload can either directly be an array or an object containing a `values` field and
56/// arbitrary other fields. All other fields will be collected into `Values::data` when
57/// deserializing and re-serialized in the same place. The shorthand array notation is always
58/// reserialized as object.
59#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
60pub struct Values<T> {
61    /// The values of the collection.
62    pub values: Vec<T>,
63}
64
65impl<T> Values<T> {
66    /// Creates an empty values struct.
67    pub fn new() -> Values<T> {
68        Values { values: Vec::new() }
69    }
70
71    /// Checks whether this struct is empty in both values and data.
72    pub fn is_empty(&self) -> bool {
73        self.values.is_empty()
74    }
75}
76
77impl<T> Default for Values<T> {
78    fn default() -> Self {
79        // Default implemented manually even if <T> does not impl Default.
80        Values::new()
81    }
82}
83
84impl<T> From<Vec<T>> for Values<T> {
85    fn from(values: Vec<T>) -> Self {
86        Values { values }
87    }
88}
89
90impl<T> AsRef<[T]> for Values<T> {
91    fn as_ref(&self) -> &[T] {
92        &self.values
93    }
94}
95
96impl<T> AsMut<Vec<T>> for Values<T> {
97    fn as_mut(&mut self) -> &mut Vec<T> {
98        &mut self.values
99    }
100}
101
102impl<T> ops::Deref for Values<T> {
103    type Target = [T];
104
105    fn deref(&self) -> &Self::Target {
106        &self.values
107    }
108}
109
110impl<T> ops::DerefMut for Values<T> {
111    fn deref_mut(&mut self) -> &mut Self::Target {
112        &mut self.values
113    }
114}
115
116impl<T> FromIterator<T> for Values<T> {
117    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
118        Vec::<T>::from_iter(iter).into()
119    }
120}
121
122impl<T> Extend<T> for Values<T> {
123    fn extend<I>(&mut self, iter: I)
124    where
125        I: IntoIterator<Item = T>,
126    {
127        self.values.extend(iter)
128    }
129}
130
131impl<'a, T> IntoIterator for &'a mut Values<T> {
132    type Item = <&'a mut Vec<T> as IntoIterator>::Item;
133    type IntoIter = <&'a mut Vec<T> as IntoIterator>::IntoIter;
134
135    fn into_iter(self) -> Self::IntoIter {
136        self.values.iter_mut()
137    }
138}
139
140impl<'a, T> IntoIterator for &'a Values<T> {
141    type Item = <&'a Vec<T> as IntoIterator>::Item;
142    type IntoIter = <&'a Vec<T> as IntoIterator>::IntoIter;
143
144    fn into_iter(self) -> Self::IntoIter {
145        self.values.iter()
146    }
147}
148
149impl<T> IntoIterator for Values<T> {
150    type Item = <Vec<T> as IntoIterator>::Item;
151    type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
152
153    fn into_iter(self) -> Self::IntoIter {
154        self.values.into_iter()
155    }
156}
157
158/// Represents a log entry message.
159///
160/// A log message is similar to the `message` attribute on the event itself but
161/// can additionally hold optional parameters.
162#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
163pub struct LogEntry {
164    /// The log message with parameters replaced by `%s`
165    pub message: String,
166    /// Positional parameters to be inserted into the log entry.
167    #[serde(default, skip_serializing_if = "Vec::is_empty")]
168    pub params: Vec<Value>,
169}
170
171/// Represents a frame.
172#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
173pub struct Frame {
174    /// The name of the function is known.
175    ///
176    /// Note that this might include the name of a class as well if that makes
177    /// sense for the language.
178    #[serde(default, skip_serializing_if = "Option::is_none")]
179    pub function: Option<String>,
180    /// The potentially mangled name of the symbol as it appears in an executable.
181    ///
182    /// This is different from a function name by generally being the mangled
183    /// name that appears natively in the binary.  This is relevant for languages
184    /// like Swift, C++ or Rust.
185    #[serde(default, skip_serializing_if = "Option::is_none")]
186    pub symbol: Option<String>,
187    /// The name of the module the frame is contained in.
188    ///
189    /// Note that this might also include a class name if that is something the
190    /// language natively considers to be part of the stack (for instance in Java).
191    #[serde(default, skip_serializing_if = "Option::is_none")]
192    pub module: Option<String>,
193    /// The name of the package that contains the frame.
194    ///
195    /// For instance this can be a dylib for native languages, the name of the jar
196    /// or .NET assembly.
197    #[serde(default, skip_serializing_if = "Option::is_none")]
198    pub package: Option<String>,
199    /// The filename (basename only).
200    #[serde(default, skip_serializing_if = "Option::is_none")]
201    pub filename: Option<String>,
202    /// If known the absolute path.
203    #[serde(default, skip_serializing_if = "Option::is_none")]
204    pub abs_path: Option<String>,
205    /// The line number if known.
206    #[serde(default, skip_serializing_if = "Option::is_none")]
207    pub lineno: Option<u64>,
208    /// The column number if known.
209    #[serde(default, skip_serializing_if = "Option::is_none")]
210    pub colno: Option<u64>,
211    /// The sources of the lines leading up to the current line.
212    #[serde(default, skip_serializing_if = "Vec::is_empty")]
213    pub pre_context: Vec<String>,
214    /// The current line as source.
215    #[serde(default, skip_serializing_if = "Option::is_none")]
216    pub context_line: Option<String>,
217    /// The sources of the lines after the current line.
218    #[serde(default, skip_serializing_if = "Vec::is_empty")]
219    pub post_context: Vec<String>,
220    /// In-app indicator.
221    #[serde(default, skip_serializing_if = "Option::is_none")]
222    pub in_app: Option<bool>,
223    /// Optional local variables.
224    #[serde(default, skip_serializing_if = "Map::is_empty")]
225    pub vars: Map<String, Value>,
226    /// If known the location of the image.
227    #[serde(default, skip_serializing_if = "Option::is_none")]
228    pub image_addr: Option<Addr>,
229    /// If known the location of the instruction.
230    #[serde(default, skip_serializing_if = "Option::is_none")]
231    pub instruction_addr: Option<Addr>,
232    /// If known the location of symbol.
233    #[serde(default, skip_serializing_if = "Option::is_none")]
234    pub symbol_addr: Option<Addr>,
235    /// Optionally changes the addressing mode. The default value is the same as
236    /// `"abs"` which means absolute referencing. This can also be set to
237    /// `"rel:DEBUG_ID"` or `"rel:IMAGE_INDEX"` to make addresses relative to an
238    /// object referenced by debug id or index. This for instance is necessary
239    /// for WASM processing as WASM does not use a unified address space.
240    #[serde(default, skip_serializing_if = "Option::is_none")]
241    pub addr_mode: Option<String>,
242}
243
244/// Represents template debug info.
245#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
246pub struct TemplateInfo {
247    /// The filename (basename only).
248    #[serde(default, skip_serializing_if = "Option::is_none")]
249    pub filename: Option<String>,
250    /// If known the absolute path.
251    #[serde(default, skip_serializing_if = "Option::is_none")]
252    pub abs_path: Option<String>,
253    /// The line number if known.
254    #[serde(default, skip_serializing_if = "Option::is_none")]
255    pub lineno: Option<u64>,
256    /// The column number if known.
257    #[serde(default, skip_serializing_if = "Option::is_none")]
258    pub colno: Option<u64>,
259    /// The sources of the lines leading up to the current line.
260    #[serde(default, skip_serializing_if = "Vec::is_empty")]
261    pub pre_context: Vec<String>,
262    /// The current line as source.
263    #[serde(default, skip_serializing_if = "Option::is_none")]
264    pub context_line: Option<String>,
265    /// The sources of the lines after the current line.
266    #[serde(default, skip_serializing_if = "Vec::is_empty")]
267    pub post_context: Vec<String>,
268}
269
270/// Represents a stacktrace.
271#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
272pub struct Stacktrace {
273    /// The list of frames in the stacktrace.
274    #[serde(default)]
275    pub frames: Vec<Frame>,
276    /// Optionally a segment of frames removed (`start`, `end`).
277    #[serde(default, skip_serializing_if = "Option::is_none")]
278    pub frames_omitted: Option<(u64, u64)>,
279    /// Optional register values of the thread.
280    #[serde(default, skip_serializing_if = "Map::is_empty")]
281    pub registers: Map<String, RegVal>,
282}
283
284impl Stacktrace {
285    /// Optionally creates a stacktrace from a list of stack frames.
286    pub fn from_frames_reversed(mut frames: Vec<Frame>) -> Option<Stacktrace> {
287        if frames.is_empty() {
288            None
289        } else {
290            frames.reverse();
291            Some(Stacktrace {
292                frames,
293                ..Default::default()
294            })
295        }
296    }
297}
298
299/// Represents a thread id.
300#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
301#[serde(untagged)]
302pub enum ThreadId {
303    /// Integer representation for the thread id
304    Int(u64),
305    /// String representation for the thread id
306    String(String),
307}
308
309impl Default for ThreadId {
310    fn default() -> ThreadId {
311        ThreadId::Int(0)
312    }
313}
314
315impl<'a> From<&'a str> for ThreadId {
316    fn from(id: &'a str) -> ThreadId {
317        ThreadId::String(id.to_string())
318    }
319}
320
321impl From<String> for ThreadId {
322    fn from(id: String) -> ThreadId {
323        ThreadId::String(id)
324    }
325}
326
327impl From<i64> for ThreadId {
328    fn from(id: i64) -> ThreadId {
329        ThreadId::Int(id as u64)
330    }
331}
332
333impl From<i32> for ThreadId {
334    fn from(id: i32) -> ThreadId {
335        ThreadId::Int(id as u64)
336    }
337}
338
339impl From<u32> for ThreadId {
340    fn from(id: u32) -> ThreadId {
341        ThreadId::Int(id as u64)
342    }
343}
344
345impl From<u16> for ThreadId {
346    fn from(id: u16) -> ThreadId {
347        ThreadId::Int(id as u64)
348    }
349}
350
351impl fmt::Display for ThreadId {
352    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
353        match *self {
354            ThreadId::Int(i) => write!(f, "{i}"),
355            ThreadId::String(ref s) => write!(f, "{s}"),
356        }
357    }
358}
359
360/// Represents an address.
361#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
362pub struct Addr(pub u64);
363
364impl Addr {
365    /// Returns `true` if this address is the null pointer.
366    pub fn is_null(&self) -> bool {
367        self.0 == 0
368    }
369}
370
371impl_hex_serde!(Addr, u64);
372
373impl From<u64> for Addr {
374    fn from(addr: u64) -> Addr {
375        Addr(addr)
376    }
377}
378
379impl From<i32> for Addr {
380    fn from(addr: i32) -> Addr {
381        Addr(addr as u64)
382    }
383}
384
385impl From<u32> for Addr {
386    fn from(addr: u32) -> Addr {
387        Addr(addr as u64)
388    }
389}
390
391impl From<usize> for Addr {
392    fn from(addr: usize) -> Addr {
393        Addr(addr as u64)
394    }
395}
396
397impl<T> From<*const T> for Addr {
398    fn from(addr: *const T) -> Addr {
399        Addr(addr as u64)
400    }
401}
402
403impl<T> From<*mut T> for Addr {
404    fn from(addr: *mut T) -> Addr {
405        Addr(addr as u64)
406    }
407}
408
409impl From<Addr> for u64 {
410    fn from(addr: Addr) -> Self {
411        addr.0
412    }
413}
414
415fn is_false(value: &bool) -> bool {
416    !*value
417}
418
419/// Represents a register value.
420#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
421pub struct RegVal(pub u64);
422
423impl_hex_serde!(RegVal, u64);
424
425impl From<u64> for RegVal {
426    fn from(addr: u64) -> RegVal {
427        RegVal(addr)
428    }
429}
430
431impl From<i32> for RegVal {
432    fn from(addr: i32) -> RegVal {
433        RegVal(addr as u64)
434    }
435}
436
437impl From<u32> for RegVal {
438    fn from(addr: u32) -> RegVal {
439        RegVal(addr as u64)
440    }
441}
442
443impl From<usize> for RegVal {
444    fn from(addr: usize) -> RegVal {
445        RegVal(addr as u64)
446    }
447}
448
449impl<T> From<*const T> for RegVal {
450    fn from(addr: *const T) -> RegVal {
451        RegVal(addr as u64)
452    }
453}
454
455impl<T> From<*mut T> for RegVal {
456    fn from(addr: *mut T) -> RegVal {
457        RegVal(addr as u64)
458    }
459}
460
461impl From<RegVal> for u64 {
462    fn from(reg: RegVal) -> Self {
463        reg.0
464    }
465}
466
467/// Represents a single thread.
468#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
469pub struct Thread {
470    /// The optional ID of the thread (usually an integer)
471    #[serde(default, skip_serializing_if = "Option::is_none")]
472    pub id: Option<ThreadId>,
473    /// The optional name of the thread.
474    #[serde(default, skip_serializing_if = "Option::is_none")]
475    pub name: Option<String>,
476    /// If the thread suspended or crashed a stacktrace can be
477    /// attached here.
478    #[serde(default, skip_serializing_if = "Option::is_none")]
479    pub stacktrace: Option<Stacktrace>,
480    /// Optional raw stacktrace.
481    #[serde(default, skip_serializing_if = "Option::is_none")]
482    pub raw_stacktrace: Option<Stacktrace>,
483    /// True if this is the crashed thread.
484    #[serde(default, skip_serializing_if = "is_false")]
485    pub crashed: bool,
486    /// Indicates that the thread was not suspended when the
487    /// event was created.
488    #[serde(default, skip_serializing_if = "is_false")]
489    pub current: bool,
490}
491
492/// POSIX signal with optional extended data.
493#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
494pub struct CError {
495    /// The error code as specified by ISO C99, POSIX.1-2001 or POSIX.1-2008.
496    pub number: i32,
497    /// Optional name of the errno constant.
498    #[serde(default, skip_serializing_if = "Option::is_none")]
499    pub name: Option<String>,
500}
501
502impl From<i32> for CError {
503    fn from(number: i32) -> CError {
504        CError { number, name: None }
505    }
506}
507
508impl From<CError> for i32 {
509    fn from(err: CError) -> Self {
510        err.number
511    }
512}
513
514/// Mach exception information.
515#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
516pub struct MachException {
517    /// The mach exception type.
518    pub exception: i32,
519    /// The mach exception code.
520    pub code: u64,
521    /// The mach exception subcode.
522    pub subcode: u64,
523    /// Optional name of the mach exception.
524    #[serde(default, skip_serializing_if = "Option::is_none")]
525    pub name: Option<String>,
526}
527
528/// POSIX signal with optional extended data.
529#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
530pub struct PosixSignal {
531    /// The POSIX signal number.
532    pub number: i32,
533    /// An optional signal code present on Apple systems.
534    #[serde(default, skip_serializing_if = "Option::is_none")]
535    pub code: Option<i32>,
536    /// Optional name of the errno constant.
537    #[serde(default, skip_serializing_if = "Option::is_none")]
538    pub name: Option<String>,
539    /// Optional name of the errno constant.
540    #[serde(default, skip_serializing_if = "Option::is_none")]
541    pub code_name: Option<String>,
542}
543
544impl From<i32> for PosixSignal {
545    fn from(number: i32) -> PosixSignal {
546        PosixSignal {
547            number,
548            code: None,
549            name: None,
550            code_name: None,
551        }
552    }
553}
554
555impl From<(i32, i32)> for PosixSignal {
556    fn from(tuple: (i32, i32)) -> PosixSignal {
557        let (number, code) = tuple;
558        PosixSignal {
559            number,
560            code: Some(code),
561            name: None,
562            code_name: None,
563        }
564    }
565}
566
567impl From<PosixSignal> for i32 {
568    fn from(sig: PosixSignal) -> Self {
569        sig.number
570    }
571}
572
573/// Operating system or runtime meta information to an exception mechanism.
574#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
575pub struct MechanismMeta {
576    /// Optional ISO C standard error code.
577    #[serde(default, skip_serializing_if = "Option::is_none")]
578    pub errno: Option<CError>,
579    /// Optional POSIX signal number.
580    #[serde(default, skip_serializing_if = "Option::is_none")]
581    pub signal: Option<PosixSignal>,
582    /// Optional mach exception information.
583    #[serde(default, skip_serializing_if = "Option::is_none")]
584    pub mach_exception: Option<MachException>,
585}
586
587impl MechanismMeta {
588    fn is_empty(&self) -> bool {
589        self.errno.is_none() && self.signal.is_none() && self.mach_exception.is_none()
590    }
591}
592
593/// Represents a single exception.
594#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
595pub struct Mechanism {
596    /// The mechanism type identifier.
597    #[serde(rename = "type")]
598    pub ty: String,
599    /// Human readable detail description.
600    #[serde(default, skip_serializing_if = "Option::is_none")]
601    pub description: Option<String>,
602    /// An optional link to online resources describing this error.
603    #[serde(default, skip_serializing_if = "Option::is_none")]
604    pub help_link: Option<Url>,
605    /// An optional flag indicating whether this exception was handled.
606    #[serde(default, skip_serializing_if = "Option::is_none")]
607    pub handled: Option<bool>,
608    /// An optional flag indicating a synthetic exception.
609    #[serde(default, skip_serializing_if = "Option::is_none")]
610    pub synthetic: Option<bool>,
611    /// Additional attributes depending on the mechanism type.
612    #[serde(default, skip_serializing_if = "Map::is_empty")]
613    pub data: Map<String, Value>,
614    /// Operating system or runtime meta information.
615    #[serde(default, skip_serializing_if = "MechanismMeta::is_empty")]
616    pub meta: MechanismMeta,
617}
618
619/// Represents a single exception.
620#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
621pub struct Exception {
622    /// The type of the exception.
623    #[serde(rename = "type")]
624    pub ty: String,
625    /// The optional value of the exception.
626    #[serde(skip_serializing_if = "Option::is_none")]
627    pub value: Option<String>,
628    /// An optional module for this exception.
629    #[serde(default, skip_serializing_if = "Option::is_none")]
630    pub module: Option<String>,
631    /// Optionally the stacktrace.
632    #[serde(default, skip_serializing_if = "Option::is_none")]
633    pub stacktrace: Option<Stacktrace>,
634    /// An optional raw stacktrace.
635    #[serde(default, skip_serializing_if = "Option::is_none")]
636    pub raw_stacktrace: Option<Stacktrace>,
637    /// Optional identifier referring to a thread.
638    #[serde(default, skip_serializing_if = "Option::is_none")]
639    pub thread_id: Option<ThreadId>,
640    /// The mechanism of the exception including OS specific exception values.
641    #[serde(default, skip_serializing_if = "Option::is_none")]
642    pub mechanism: Option<Mechanism>,
643}
644
645/// An error used when parsing `Level`.
646#[derive(Debug, Error)]
647#[error("invalid level")]
648pub struct ParseLevelError;
649
650/// Represents the level of severity of an event or breadcrumb.
651#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
652pub enum Level {
653    /// Indicates very spammy debug information.
654    Debug,
655    /// Informational messages.
656    #[default]
657    Info,
658    /// A warning.
659    Warning,
660    /// An error.
661    Error,
662    /// Similar to error but indicates a critical event that usually causes a shutdown.
663    Fatal,
664}
665
666impl str::FromStr for Level {
667    type Err = ParseLevelError;
668
669    fn from_str(string: &str) -> Result<Level, Self::Err> {
670        Ok(match string {
671            "debug" => Level::Debug,
672            "info" | "log" => Level::Info,
673            "warning" => Level::Warning,
674            "error" => Level::Error,
675            "fatal" => Level::Fatal,
676            _ => return Err(ParseLevelError),
677        })
678    }
679}
680
681impl fmt::Display for Level {
682    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
683        match *self {
684            Level::Debug => write!(f, "debug"),
685            Level::Info => write!(f, "info"),
686            Level::Warning => write!(f, "warning"),
687            Level::Error => write!(f, "error"),
688            Level::Fatal => write!(f, "fatal"),
689        }
690    }
691}
692
693impl Level {
694    /// A quick way to check if the level is `debug`.
695    pub fn is_debug(&self) -> bool {
696        *self == Level::Debug
697    }
698
699    /// A quick way to check if the level is `info`.
700    pub fn is_info(&self) -> bool {
701        *self == Level::Info
702    }
703
704    /// A quick way to check if the level is `warning`.
705    pub fn is_warning(&self) -> bool {
706        *self == Level::Warning
707    }
708
709    /// A quick way to check if the level is `error`.
710    pub fn is_error(&self) -> bool {
711        *self == Level::Error
712    }
713
714    /// A quick way to check if the level is `fatal`.
715    pub fn is_fatal(&self) -> bool {
716        *self == Level::Fatal
717    }
718}
719
720impl_str_serde!(Level);
721
722mod breadcrumb {
723    use super::*;
724
725    pub fn default_type() -> String {
726        "default".to_string()
727    }
728
729    pub fn is_default_type(ty: &str) -> bool {
730        ty == "default"
731    }
732
733    pub fn default_level() -> Level {
734        Level::Info
735    }
736}
737
738/// Represents a single breadcrumb.
739#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
740pub struct Breadcrumb {
741    /// The timestamp of the breadcrumb.  This is required.
742    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
743    pub timestamp: SystemTime,
744    /// The type of the breadcrumb.
745    #[serde(
746        rename = "type",
747        default = "breadcrumb::default_type",
748        skip_serializing_if = "breadcrumb::is_default_type"
749    )]
750    pub ty: String,
751    /// The optional category of the breadcrumb.
752    #[serde(default, skip_serializing_if = "Option::is_none")]
753    pub category: Option<String>,
754    /// The non optional level of the breadcrumb.  It
755    /// defaults to info.
756    #[serde(
757        default = "breadcrumb::default_level",
758        skip_serializing_if = "Level::is_info"
759    )]
760    pub level: Level,
761    /// An optional human readbale message for the breadcrumb.
762    #[serde(default, skip_serializing_if = "Option::is_none")]
763    pub message: Option<String>,
764    /// Arbitrary breadcrumb data that should be send along.
765    #[serde(default, skip_serializing_if = "Map::is_empty")]
766    pub data: Map<String, Value>,
767}
768
769impl Default for Breadcrumb {
770    fn default() -> Breadcrumb {
771        Breadcrumb {
772            timestamp: SystemTime::now(),
773            ty: breadcrumb::default_type(),
774            category: Default::default(),
775            level: breadcrumb::default_level(),
776            message: Default::default(),
777            data: Default::default(),
778        }
779    }
780}
781
782/// An IP address, either IPv4, IPv6 or Auto.
783#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Default)]
784pub enum IpAddress {
785    /// The IP address needs to be inferred from the user's context.
786    #[default]
787    Auto,
788    /// The exact given IP address (v4 or v6).
789    Exact(IpAddr),
790}
791
792impl PartialEq<IpAddr> for IpAddress {
793    fn eq(&self, other: &IpAddr) -> bool {
794        match *self {
795            IpAddress::Auto => false,
796            IpAddress::Exact(ref addr) => addr == other,
797        }
798    }
799}
800
801impl cmp::PartialOrd<IpAddr> for IpAddress {
802    fn partial_cmp(&self, other: &IpAddr) -> Option<cmp::Ordering> {
803        match *self {
804            IpAddress::Auto => None,
805            IpAddress::Exact(ref addr) => addr.partial_cmp(other),
806        }
807    }
808}
809
810impl fmt::Display for IpAddress {
811    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
812        match *self {
813            IpAddress::Auto => write!(f, "{{{{auto}}}}"),
814            IpAddress::Exact(ref addr) => write!(f, "{addr}"),
815        }
816    }
817}
818
819impl From<IpAddr> for IpAddress {
820    fn from(addr: IpAddr) -> IpAddress {
821        IpAddress::Exact(addr)
822    }
823}
824
825impl str::FromStr for IpAddress {
826    type Err = AddrParseError;
827
828    fn from_str(string: &str) -> Result<IpAddress, AddrParseError> {
829        match string {
830            "{{auto}}" => Ok(IpAddress::Auto),
831            other => other.parse().map(IpAddress::Exact),
832        }
833    }
834}
835
836impl_str_serde!(IpAddress);
837
838/// Represents user info.
839#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
840pub struct User {
841    /// The ID of the user.
842    #[serde(default, skip_serializing_if = "Option::is_none")]
843    pub id: Option<String>,
844    /// The email address of the user.
845    #[serde(default, skip_serializing_if = "Option::is_none")]
846    pub email: Option<String>,
847    /// The remote ip address of the user.
848    #[serde(default, skip_serializing_if = "Option::is_none")]
849    pub ip_address: Option<IpAddress>,
850    /// A human readable username of the user.
851    #[serde(default, skip_serializing_if = "Option::is_none")]
852    pub username: Option<String>,
853    /// Additional arbitrary fields for forwards compatibility.
854    #[serde(flatten)]
855    pub other: Map<String, Value>,
856}
857
858/// Represents http request data.
859#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
860pub struct Request {
861    /// The current URL of the request.
862    #[serde(default, skip_serializing_if = "Option::is_none")]
863    pub url: Option<Url>,
864    /// The HTTP request method.
865    #[serde(default, skip_serializing_if = "Option::is_none")]
866    pub method: Option<String>,
867    /// Optionally some associated request data (human readable)
868    // XXX: this makes absolutely no sense because of unicode
869    #[serde(default, skip_serializing_if = "Option::is_none")]
870    pub data: Option<String>,
871    /// Optionally the encoded query string.
872    #[serde(default, skip_serializing_if = "Option::is_none")]
873    pub query_string: Option<String>,
874    /// An encoded cookie string if available.
875    #[serde(default, skip_serializing_if = "Option::is_none")]
876    pub cookies: Option<String>,
877    /// HTTP request headers.
878    #[serde(default, skip_serializing_if = "Map::is_empty")]
879    pub headers: Map<String, String>,
880    /// Optionally a CGI/WSGI etc. environment dictionary.
881    #[serde(default, skip_serializing_if = "Map::is_empty")]
882    pub env: Map<String, String>,
883}
884
885/// Holds information about the system SDK.
886///
887/// This is relevant for iOS and other platforms that have a system
888/// SDK.  Not to be confused with the client SDK.
889#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
890pub struct SystemSdkInfo {
891    /// The internal name of the SDK
892    pub sdk_name: String,
893    /// the major version of the SDK as integer or 0
894    pub version_major: u32,
895    /// the minor version of the SDK as integer or 0
896    pub version_minor: u32,
897    /// the patch version of the SDK as integer or 0
898    pub version_patchlevel: u32,
899}
900
901/// Represents a debug image.
902#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
903#[serde(rename_all = "snake_case", tag = "type")]
904pub enum DebugImage {
905    /// Apple debug images (machos).  This is currently also used for
906    /// non apple platforms with similar debug setups.
907    Apple(AppleDebugImage),
908    /// Symbolic (new style) debug infos.
909    Symbolic(SymbolicDebugImage),
910    /// A reference to a proguard debug file.
911    Proguard(ProguardDebugImage),
912    /// Image used for WebAssembly. Their structure is identical to other native
913    /// images.
914    Wasm(WasmDebugImage),
915}
916
917impl DebugImage {
918    /// Returns the name of the type on sentry.
919    pub fn type_name(&self) -> &str {
920        match *self {
921            DebugImage::Apple(..) => "apple",
922            DebugImage::Symbolic(..) => "symbolic",
923            DebugImage::Proguard(..) => "proguard",
924            DebugImage::Wasm(..) => "wasm",
925        }
926    }
927}
928
929macro_rules! into_debug_image {
930    ($kind:ident, $ty:ty) => {
931        impl From<$ty> for DebugImage {
932            fn from(data: $ty) -> DebugImage {
933                DebugImage::$kind(data)
934            }
935        }
936    };
937}
938
939/// Represents an apple debug image in the debug meta.
940#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
941pub struct AppleDebugImage {
942    /// The name of the debug image (usually filename)
943    pub name: String,
944    /// The optional CPU architecture of the debug image.
945    pub arch: Option<String>,
946    /// Alternatively a macho cpu type.
947    pub cpu_type: Option<u32>,
948    /// Alternatively a macho cpu subtype.
949    pub cpu_subtype: Option<u32>,
950    /// The starting address of the image.
951    pub image_addr: Addr,
952    /// The size of the image in bytes.
953    pub image_size: u64,
954    /// The address where the image is loaded at runtime.
955    #[serde(default, skip_serializing_if = "Addr::is_null")]
956    pub image_vmaddr: Addr,
957    /// The unique UUID of the image.
958    pub uuid: Uuid,
959}
960
961/// Represents a symbolic debug image.
962#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
963pub struct SymbolicDebugImage {
964    /// Path and name of the image file (required).
965    ///
966    /// The absolute path to the dynamic library or executable. This helps to locate the file if it is missing on Sentry.
967    /// This is also called `code_file`.
968    pub name: String,
969    /// The optional CPU architecture of the debug image.
970    pub arch: Option<String>,
971    /// Starting memory address of the image (required).
972    ///
973    /// Memory address, at which the image is mounted in the virtual address space of the process.
974    pub image_addr: Addr,
975    /// Size of the image in bytes (required).
976    ///
977    /// The size of the image in virtual memory.
978    pub image_size: u64,
979    /// Loading address in virtual memory.
980    ///
981    /// Preferred load address of the image in virtual memory, as declared in the headers of the
982    /// image. When loading an image, the operating system may still choose to place it at a
983    /// different address.
984    ///
985    /// Symbols and addresses in the native image are always relative to the start of the image and do not consider the preferred load address. It is merely a hint to the loader.
986    #[serde(default, skip_serializing_if = "Addr::is_null")]
987    pub image_vmaddr: Addr,
988    /// Unique debug identifier of the image.
989    ///
990    /// This is also called `debug_id`.
991    pub id: DebugId,
992
993    /// Optional identifier of the code file.
994    #[serde(default, skip_serializing_if = "Option::is_none")]
995    pub code_id: Option<CodeId>,
996    /// Path and name of the debug companion file.
997    #[serde(default, skip_serializing_if = "Option::is_none")]
998    pub debug_file: Option<String>,
999}
1000
1001/// Represents a proguard mapping file reference.
1002#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1003pub struct ProguardDebugImage {
1004    /// The UUID of the associated proguard file.
1005    pub uuid: Uuid,
1006}
1007
1008/// Represents a WebAssembly debug image.
1009#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1010pub struct WasmDebugImage {
1011    /// The name of the debug image (usually filename)
1012    pub name: String,
1013    /// Identifier of the dynamic library or executable.
1014    pub debug_id: Uuid,
1015    /// Name or absolute URL to the WASM file containing debug information for
1016    /// this image. This value might be required to retrieve debug files from
1017    /// certain symbol servers. This should correspond to the externalized URL
1018    /// pulled from the external_debug_info custom section.
1019    #[serde(default, skip_serializing_if = "Option::is_none")]
1020    pub debug_file: Option<String>,
1021    /// Identifier of the WASM file. It is the value of the build_id custom
1022    /// section formatted as HEX string.
1023    #[serde(default, skip_serializing_if = "Option::is_none")]
1024    pub code_id: Option<String>,
1025    /// The absolute URL to the wasm file. This helps to locate the file if it
1026    /// is missing on Sentry.
1027    pub code_file: String,
1028}
1029
1030into_debug_image!(Apple, AppleDebugImage);
1031into_debug_image!(Symbolic, SymbolicDebugImage);
1032into_debug_image!(Proguard, ProguardDebugImage);
1033into_debug_image!(Wasm, WasmDebugImage);
1034
1035/// Represents debug meta information.
1036#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
1037pub struct DebugMeta {
1038    /// Optional system SDK information.
1039    #[serde(default, skip_serializing_if = "Option::is_none")]
1040    pub sdk_info: Option<SystemSdkInfo>,
1041    /// A list of debug information files.
1042    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1043    pub images: Vec<DebugImage>,
1044}
1045
1046impl DebugMeta {
1047    /// Returns true if the debug meta is empty.
1048    ///
1049    /// This is used by the serializer to entirely skip the section.
1050    pub fn is_empty(&self) -> bool {
1051        self.sdk_info.is_none() && self.images.is_empty()
1052    }
1053}
1054
1055/// Information on the SDK client.
1056#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1057pub struct ClientSdkInfo {
1058    /// The name of the SDK.
1059    pub name: String,
1060    /// The version of the SDK.
1061    pub version: String,
1062    /// An optional list of integrations that are enabled in this SDK.
1063    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1064    pub integrations: Vec<String>,
1065    /// An optional list of packages that are installed in the SDK's environment.
1066    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1067    pub packages: Vec<ClientSdkPackage>,
1068}
1069
1070/// Represents an installed package relevant to the SDK.
1071#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1072pub struct ClientSdkPackage {
1073    /// The name of the package installed.
1074    pub name: String,
1075    /// The version of the package.
1076    pub version: String,
1077}
1078
1079/// Typed contextual data.
1080///
1081/// Types like `OsContext` can be directly converted with `.into()`
1082/// to `Context`.
1083#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1084#[serde(rename_all = "snake_case", tag = "type")]
1085#[non_exhaustive]
1086pub enum Context {
1087    /// Device data.
1088    Device(Box<DeviceContext>),
1089    /// Operating system data.
1090    Os(Box<OsContext>),
1091    /// Runtime data.
1092    Runtime(Box<RuntimeContext>),
1093    /// Application data.
1094    App(Box<AppContext>),
1095    /// Web browser data.
1096    Browser(Box<BrowserContext>),
1097    /// Tracing data.
1098    Trace(Box<TraceContext>),
1099    /// GPU data.
1100    Gpu(Box<GpuContext>),
1101    /// OpenTelemetry data.
1102    Otel(Box<OtelContext>),
1103    /// HTTP response data.
1104    Response(Box<ResponseContext>),
1105    /// Generic other context data.
1106    #[serde(rename = "unknown")]
1107    Other(Map<String, Value>),
1108}
1109
1110impl Context {
1111    /// Returns the name of the type for sentry.
1112    pub fn type_name(&self) -> &str {
1113        match *self {
1114            Context::Device(..) => "device",
1115            Context::Os(..) => "os",
1116            Context::Runtime(..) => "runtime",
1117            Context::App(..) => "app",
1118            Context::Browser(..) => "browser",
1119            Context::Trace(..) => "trace",
1120            Context::Gpu(..) => "gpu",
1121            Context::Otel(..) => "otel",
1122            Context::Response(..) => "response",
1123            Context::Other(..) => "unknown",
1124        }
1125    }
1126}
1127
1128/// Optional device screen orientation
1129#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
1130#[serde(rename_all = "lowercase")]
1131pub enum Orientation {
1132    /// Portrait device orientation.
1133    Portrait,
1134    /// Landscape device orientation.
1135    Landscape,
1136}
1137
1138/// Holds device information.
1139#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1140pub struct DeviceContext {
1141    /// The name of the device.
1142    #[serde(default, skip_serializing_if = "Option::is_none")]
1143    pub name: Option<String>,
1144    /// The family of the device model.
1145    #[serde(default, skip_serializing_if = "Option::is_none")]
1146    pub family: Option<String>,
1147    /// The device model (human readable).
1148    #[serde(default, skip_serializing_if = "Option::is_none")]
1149    pub model: Option<String>,
1150    /// The device model (internal identifier).
1151    #[serde(default, skip_serializing_if = "Option::is_none")]
1152    pub model_id: Option<String>,
1153    /// The native cpu architecture of the device.
1154    #[serde(default, skip_serializing_if = "Option::is_none")]
1155    pub arch: Option<String>,
1156    /// The current battery level (0-100).
1157    #[serde(default, skip_serializing_if = "Option::is_none")]
1158    pub battery_level: Option<f32>,
1159    /// The current screen orientation.
1160    #[serde(default, skip_serializing_if = "Option::is_none")]
1161    pub orientation: Option<Orientation>,
1162    /// Simulator/prod indicator.
1163    #[serde(default, skip_serializing_if = "Option::is_none")]
1164    pub simulator: Option<bool>,
1165    /// Total memory available in byts.
1166    #[serde(default, skip_serializing_if = "Option::is_none")]
1167    pub memory_size: Option<u64>,
1168    /// How much memory is still available in bytes.
1169    #[serde(default, skip_serializing_if = "Option::is_none")]
1170    pub free_memory: Option<u64>,
1171    /// How much memory is usable for the app in bytes.
1172    #[serde(default, skip_serializing_if = "Option::is_none")]
1173    pub usable_memory: Option<u64>,
1174    /// Total storage size of the device in bytes.
1175    #[serde(default, skip_serializing_if = "Option::is_none")]
1176    pub storage_size: Option<u64>,
1177    /// How much storage is free in bytes.
1178    #[serde(default, skip_serializing_if = "Option::is_none")]
1179    pub free_storage: Option<u64>,
1180    /// Total size of the attached external storage in bytes (eg: android SDK card).
1181    #[serde(default, skip_serializing_if = "Option::is_none")]
1182    pub external_storage_size: Option<u64>,
1183    /// Free size of the attached external storage in bytes (eg: android SDK card).
1184    #[serde(default, skip_serializing_if = "Option::is_none")]
1185    pub external_free_storage: Option<u64>,
1186    /// Optionally an indicator when the device was booted.
1187    #[serde(
1188        default,
1189        skip_serializing_if = "Option::is_none",
1190        with = "ts_rfc3339_opt"
1191    )]
1192    pub boot_time: Option<SystemTime>,
1193    /// The timezone of the device.
1194    #[serde(default, skip_serializing_if = "Option::is_none")]
1195    pub timezone: Option<String>,
1196    /// Additional arbitrary fields for forwards compatibility.
1197    #[serde(flatten)]
1198    pub other: Map<String, Value>,
1199}
1200
1201/// Holds operating system information.
1202#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1203pub struct OsContext {
1204    /// The name of the operating system.
1205    #[serde(default, skip_serializing_if = "Option::is_none")]
1206    pub name: Option<String>,
1207    /// The version of the operating system.
1208    #[serde(default, skip_serializing_if = "Option::is_none")]
1209    pub version: Option<String>,
1210    /// The internal build number of the operating system.
1211    #[serde(default, skip_serializing_if = "Option::is_none")]
1212    pub build: Option<String>,
1213    /// The current kernel version.
1214    #[serde(default, skip_serializing_if = "Option::is_none")]
1215    pub kernel_version: Option<String>,
1216    /// An indicator if the os is rooted (mobile mostly).
1217    #[serde(default, skip_serializing_if = "Option::is_none")]
1218    pub rooted: Option<bool>,
1219    /// Additional arbitrary fields for forwards compatibility.
1220    #[serde(flatten)]
1221    pub other: Map<String, Value>,
1222}
1223
1224/// Holds information about the runtime.
1225#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1226pub struct RuntimeContext {
1227    /// The name of the runtime (for instance JVM).
1228    #[serde(default, skip_serializing_if = "Option::is_none")]
1229    pub name: Option<String>,
1230    /// The version of the runtime.
1231    #[serde(default, skip_serializing_if = "Option::is_none")]
1232    pub version: Option<String>,
1233    /// Additional arbitrary fields for forwards compatibility.
1234    #[serde(flatten)]
1235    pub other: Map<String, Value>,
1236}
1237
1238/// Holds app information.
1239#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1240pub struct AppContext {
1241    /// Optional start time of the app.
1242    #[serde(
1243        default,
1244        skip_serializing_if = "Option::is_none",
1245        with = "ts_rfc3339_opt"
1246    )]
1247    pub app_start_time: Option<SystemTime>,
1248    /// Optional device app hash (app specific device ID)
1249    #[serde(default, skip_serializing_if = "Option::is_none")]
1250    pub device_app_hash: Option<String>,
1251    /// Optional build identicator.
1252    #[serde(default, skip_serializing_if = "Option::is_none")]
1253    pub build_type: Option<String>,
1254    /// Optional app identifier (dotted bundle id).
1255    #[serde(default, skip_serializing_if = "Option::is_none")]
1256    pub app_identifier: Option<String>,
1257    /// Application name as it appears on the platform.
1258    #[serde(default, skip_serializing_if = "Option::is_none")]
1259    pub app_name: Option<String>,
1260    /// Application version as it appears on the platform.
1261    #[serde(default, skip_serializing_if = "Option::is_none")]
1262    pub app_version: Option<String>,
1263    /// Internal build ID as it appears on the platform.
1264    #[serde(default, skip_serializing_if = "Option::is_none")]
1265    pub app_build: Option<String>,
1266    /// Additional arbitrary fields for forwards compatibility.
1267    #[serde(flatten)]
1268    pub other: Map<String, Value>,
1269}
1270
1271/// Holds information about the web browser.
1272#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1273pub struct BrowserContext {
1274    /// The name of the browser (for instance "Chrome").
1275    #[serde(default, skip_serializing_if = "Option::is_none")]
1276    pub name: Option<String>,
1277    /// The version of the browser.
1278    #[serde(default, skip_serializing_if = "Option::is_none")]
1279    pub version: Option<String>,
1280    /// Additional arbitrary fields for forwards compatibility.
1281    #[serde(flatten)]
1282    pub other: Map<String, Value>,
1283}
1284
1285/// GPU context describes the GPU of the device.
1286#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1287pub struct GpuContext {
1288    /// The name of the graphics device.
1289    pub name: String,
1290    /// The Version of the graphics device.
1291    #[serde(default, skip_serializing_if = "Option::is_none")]
1292    pub version: Option<String>,
1293    /// The version of the graphic device driver.
1294    #[serde(default, skip_serializing_if = "Option::is_none")]
1295    pub driver_version: Option<String>,
1296    /// The PCI identifier of the graphics device.
1297    #[serde(default, skip_serializing_if = "Option::is_none")]
1298    pub id: Option<String>,
1299    /// The PCI vendor identifier of the graphics device.
1300    #[serde(default, skip_serializing_if = "Option::is_none")]
1301    pub vendor_id: Option<String>,
1302    /// The vendor name as reported by the graphics device.
1303    #[serde(default, skip_serializing_if = "Option::is_none")]
1304    pub vendor_name: Option<String>,
1305    /// The total GPU memory available in Megabytes.
1306    #[serde(default, skip_serializing_if = "Option::is_none")]
1307    pub memory_size: Option<u32>,
1308    /// The device low-level API type. Examples: "Apple Metal" or "Direct3D11"
1309    #[serde(default, skip_serializing_if = "Option::is_none")]
1310    pub api_type: Option<String>,
1311    /// Whether the GPU has multi-threaded rendering or not.
1312    #[serde(default, skip_serializing_if = "Option::is_none")]
1313    pub multi_threaded_rendering: Option<bool>,
1314    /// The Non-Power-Of-Two-Support support.
1315    #[serde(default, skip_serializing_if = "Option::is_none")]
1316    pub npot_support: Option<bool>,
1317    /// Largest size of a texture that is supported by the graphics hardware.
1318    #[serde(default, skip_serializing_if = "Option::is_none")]
1319    pub max_texture_size: Option<u32>,
1320    /// Approximate "shader capability" level of the graphics device. For example,
1321    /// `Shader Model 2.0, OpenGL ES 3.0, Metal / OpenGL ES 3.1, 27 (unknown)`.
1322    #[serde(default, skip_serializing_if = "Option::is_none")]
1323    pub graphics_shader_level: Option<String>,
1324    /// Is GPU draw call instancing supported?
1325    #[serde(default, skip_serializing_if = "Option::is_none")]
1326    pub supports_draw_call_instancing: Option<bool>,
1327    /// Is ray tracing available on the device?
1328    #[serde(default, skip_serializing_if = "Option::is_none")]
1329    pub supports_ray_tracing: Option<bool>,
1330    /// Are compute shaders available on the device?
1331    #[serde(default, skip_serializing_if = "Option::is_none")]
1332    pub supports_compute_shaders: Option<bool>,
1333    /// Are geometry shaders available on the device?
1334    #[serde(default, skip_serializing_if = "Option::is_none")]
1335    pub supports_geometry_shaders: Option<bool>,
1336    /// Additional arbitrary fields for forwards compatibility.
1337    #[serde(flatten)]
1338    pub other: Map<String, Value>,
1339}
1340
1341/// OpenTelemetry context
1342#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1343pub struct OtelContext {
1344    /// OpenTelemetry [general
1345    /// attributes](https://opentelemetry.io/docs/specs/semconv/general/attributes/).
1346    #[serde(default, skip_serializing_if = "Map::is_empty")]
1347    pub attributes: Map<String, Value>,
1348    /// OpenTelemetry [resource attributes](https://opentelemetry.io/docs/specs/semconv/resource/),
1349    /// describing the entity producing telemetry.
1350    #[serde(default, skip_serializing_if = "Map::is_empty")]
1351    pub resource: Map<String, Value>,
1352    /// Additional arbitrary fields for forwards compatibility.
1353    #[serde(flatten)]
1354    pub other: Map<String, Value>,
1355}
1356
1357/// Holds information about an HTTP response.
1358#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1359pub struct ResponseContext {
1360    /// The unparsed cookie values.
1361    #[serde(default, skip_serializing_if = "Option::is_none")]
1362    pub cookies: Option<String>,
1363    /// A map of submitted headers.
1364    ///
1365    /// If a header appears multiple times, it needs to be merged according to the HTTP standard
1366    /// for header merging. Header names are treated case-insensitively by Sentry.
1367    #[serde(default, skip_serializing_if = "Map::is_empty")]
1368    pub headers: Map<String, String>,
1369    /// The HTTP response status code.
1370    #[serde(default, skip_serializing_if = "Option::is_none")]
1371    pub status_code: Option<u64>,
1372    /// The response body size in bytes.
1373    #[serde(default, skip_serializing_if = "Option::is_none")]
1374    pub body_size: Option<u64>,
1375    /// Response data in any format that makes sense.
1376    #[serde(default, skip_serializing_if = "Option::is_none")]
1377    pub data: Option<Value>,
1378}
1379
1380/// Holds the identifier for a Span
1381#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
1382#[serde(try_from = "String", into = "String")]
1383pub struct SpanId([u8; 8]);
1384
1385impl Default for SpanId {
1386    fn default() -> Self {
1387        Self(rand::random())
1388    }
1389}
1390
1391impl fmt::Display for SpanId {
1392    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1393        write!(fmt, "{}", hex::encode(self.0))
1394    }
1395}
1396
1397impl fmt::Debug for SpanId {
1398    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1399        write!(fmt, "SpanId({self})")
1400    }
1401}
1402
1403impl From<SpanId> for String {
1404    fn from(span_id: SpanId) -> Self {
1405        span_id.to_string()
1406    }
1407}
1408
1409impl str::FromStr for SpanId {
1410    type Err = hex::FromHexError;
1411
1412    fn from_str(input: &str) -> Result<Self, Self::Err> {
1413        let mut buf = [0; 8];
1414        hex::decode_to_slice(input, &mut buf)?;
1415        Ok(Self(buf))
1416    }
1417}
1418
1419impl TryFrom<String> for SpanId {
1420    type Error = hex::FromHexError;
1421
1422    fn try_from(value: String) -> Result<Self, Self::Error> {
1423        value.parse()
1424    }
1425}
1426
1427impl From<[u8; 8]> for SpanId {
1428    fn from(value: [u8; 8]) -> Self {
1429        Self(value)
1430    }
1431}
1432
1433/// Holds the identifier for a Trace
1434#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
1435#[serde(try_from = "String", into = "String")]
1436pub struct TraceId([u8; 16]);
1437
1438impl Default for TraceId {
1439    fn default() -> Self {
1440        Self(rand::random())
1441    }
1442}
1443
1444impl fmt::Display for TraceId {
1445    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1446        write!(fmt, "{}", hex::encode(self.0))
1447    }
1448}
1449
1450impl fmt::Debug for TraceId {
1451    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1452        write!(fmt, "TraceId({self})")
1453    }
1454}
1455
1456impl From<TraceId> for String {
1457    fn from(trace_id: TraceId) -> Self {
1458        trace_id.to_string()
1459    }
1460}
1461
1462impl str::FromStr for TraceId {
1463    type Err = hex::FromHexError;
1464
1465    fn from_str(input: &str) -> Result<Self, Self::Err> {
1466        let mut buf = [0; 16];
1467        hex::decode_to_slice(input, &mut buf)?;
1468        Ok(Self(buf))
1469    }
1470}
1471
1472impl TryFrom<String> for TraceId {
1473    type Error = hex::FromHexError;
1474
1475    fn try_from(value: String) -> Result<Self, Self::Error> {
1476        value.parse()
1477    }
1478}
1479
1480impl From<[u8; 16]> for TraceId {
1481    fn from(value: [u8; 16]) -> Self {
1482        Self(value)
1483    }
1484}
1485
1486/// Holds information about a tracing event.
1487#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
1488pub struct TraceContext {
1489    /// The ID of the trace event
1490    #[serde(default)]
1491    pub span_id: SpanId,
1492    /// Determines which trace the transaction belongs to.
1493    #[serde(default)]
1494    pub trace_id: TraceId,
1495    /// Determines the parent of this transaction if any.
1496    #[serde(default, skip_serializing_if = "Option::is_none")]
1497    pub parent_span_id: Option<SpanId>,
1498    /// Short code identifying the type of operation the transaction is measuring.
1499    #[serde(default, skip_serializing_if = "Option::is_none")]
1500    pub op: Option<String>,
1501    /// Human readable detail description.
1502    #[serde(default, skip_serializing_if = "Option::is_none")]
1503    pub description: Option<String>,
1504    /// Describes the status of the span (e.g. `ok`, `cancelled`, etc.)
1505    #[serde(default, skip_serializing_if = "Option::is_none")]
1506    pub status: Option<SpanStatus>,
1507    /// Describes what created the transaction. See the [develop
1508    /// docs](https://develop.sentry.dev/sdk/telemetry/traces/trace-origin/) for more information.
1509    #[serde(default, skip_serializing_if = "Option::is_none")]
1510    pub origin: Option<String>,
1511    /// Optional data attributes to be associated with the transaction.
1512    #[serde(default, skip_serializing_if = "Map::is_empty")]
1513    pub data: Map<String, Value>,
1514}
1515
1516macro_rules! into_context {
1517    ($kind:ident, $ty:ty) => {
1518        impl From<$ty> for Context {
1519            fn from(data: $ty) -> Self {
1520                Context::$kind(Box::new(data))
1521            }
1522        }
1523    };
1524}
1525
1526into_context!(App, AppContext);
1527into_context!(Device, DeviceContext);
1528into_context!(Os, OsContext);
1529into_context!(Runtime, RuntimeContext);
1530into_context!(Browser, BrowserContext);
1531into_context!(Trace, TraceContext);
1532into_context!(Gpu, GpuContext);
1533into_context!(Otel, OtelContext);
1534into_context!(Response, ResponseContext);
1535
1536const INFERABLE_CONTEXTS: &[&str] = &[
1537    "device", "os", "runtime", "app", "browser", "trace", "gpu", "otel", "response",
1538];
1539
1540struct ContextsVisitor;
1541
1542impl<'de> de::Visitor<'de> for ContextsVisitor {
1543    type Value = Map<String, Context>;
1544
1545    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1546        formatter.write_str("contexts object")
1547    }
1548
1549    fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
1550    where
1551        A: de::MapAccess<'de>,
1552    {
1553        let mut map: Map<String, Context> = Map::new();
1554
1555        while let Some((key, mut value)) = access.next_entry::<String, Value>()? {
1556            let typed_value = value
1557                .as_object_mut()
1558                .map(|ctx| {
1559                    if !ctx.contains_key("type") {
1560                        let type_key = if INFERABLE_CONTEXTS.contains(&key.as_str()) {
1561                            key.clone().into()
1562                        } else {
1563                            Value::String("unknown".into())
1564                        };
1565                        ctx.insert(String::from("type"), type_key);
1566                    }
1567                    ctx.to_owned()
1568                })
1569                .ok_or_else(|| de::Error::custom("expected valid `context` object"))?;
1570
1571            match serde_json::from_value(serde_json::to_value(typed_value).unwrap()) {
1572                Ok(context) => {
1573                    map.insert(key, context);
1574                }
1575                Err(e) => return Err(de::Error::custom(e.to_string())),
1576            }
1577        }
1578
1579        Ok(map)
1580    }
1581}
1582
1583fn deserialize_contexts<'de, D>(deserializer: D) -> Result<Map<String, Context>, D::Error>
1584where
1585    D: Deserializer<'de>,
1586{
1587    deserializer.deserialize_map(ContextsVisitor {})
1588}
1589
1590mod event {
1591    use super::*;
1592
1593    pub fn default_id() -> Uuid {
1594        crate::random_uuid()
1595    }
1596
1597    pub fn serialize_id<S: Serializer>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
1598        serializer.serialize_some(&uuid.as_simple().to_string())
1599    }
1600
1601    pub fn default_level() -> Level {
1602        Level::Error
1603    }
1604
1605    pub fn default_platform() -> Cow<'static, str> {
1606        Cow::Borrowed("other")
1607    }
1608
1609    pub fn is_default_platform(value: &str) -> bool {
1610        value == "other"
1611    }
1612
1613    static DEFAULT_FINGERPRINT: &[Cow<'static, str>] = &[Cow::Borrowed("{{ default }}")];
1614
1615    pub fn default_fingerprint<'a>() -> Cow<'a, [Cow<'a, str>]> {
1616        Cow::Borrowed(DEFAULT_FINGERPRINT)
1617    }
1618
1619    pub fn is_default_fingerprint(fp: &[Cow<'_, str>]) -> bool {
1620        fp.len() == 1 && ((fp)[0] == "{{ default }}" || (fp)[0] == "{{default}}")
1621    }
1622}
1623
1624/// Represents a full event for Sentry.
1625#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1626pub struct Event<'a> {
1627    /// The ID of the event
1628    #[serde(default = "event::default_id", serialize_with = "event::serialize_id")]
1629    pub event_id: Uuid,
1630    /// The level of the event (defaults to error)
1631    #[serde(
1632        default = "event::default_level",
1633        skip_serializing_if = "Level::is_error"
1634    )]
1635    pub level: Level,
1636    /// An optional fingerprint configuration to override the default.
1637    #[serde(
1638        default = "event::default_fingerprint",
1639        skip_serializing_if = "event::is_default_fingerprint"
1640    )]
1641    pub fingerprint: Cow<'a, [Cow<'a, str>]>,
1642    /// The culprit of the event.
1643    #[serde(default, skip_serializing_if = "Option::is_none")]
1644    pub culprit: Option<String>,
1645    /// The transaction name of the event.
1646    #[serde(default, skip_serializing_if = "Option::is_none")]
1647    pub transaction: Option<String>,
1648    /// A message to be sent with the event.
1649    #[serde(default, skip_serializing_if = "Option::is_none")]
1650    pub message: Option<String>,
1651    /// Optionally a log entry that can be used instead of the message for
1652    /// more complex cases.
1653    #[serde(default, skip_serializing_if = "Option::is_none")]
1654    pub logentry: Option<LogEntry>,
1655    /// Optionally the name of the logger that created this event.
1656    #[serde(default, skip_serializing_if = "Option::is_none")]
1657    pub logger: Option<String>,
1658    /// Optionally a name to version mapping of installed modules.
1659    #[serde(default, skip_serializing_if = "Map::is_empty")]
1660    pub modules: Map<String, String>,
1661    /// A platform identifier for this event.
1662    #[serde(
1663        default = "event::default_platform",
1664        skip_serializing_if = "event::is_default_platform"
1665    )]
1666    pub platform: Cow<'a, str>,
1667    /// The timestamp of when the event was created.
1668    ///
1669    /// This can be set to `None` in which case the server will set a timestamp.
1670    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
1671    pub timestamp: SystemTime,
1672    /// Optionally the server (or device) name of this event.
1673    #[serde(default, skip_serializing_if = "Option::is_none")]
1674    pub server_name: Option<Cow<'a, str>>,
1675    /// A release identifier.
1676    #[serde(default, skip_serializing_if = "Option::is_none")]
1677    pub release: Option<Cow<'a, str>>,
1678    /// An optional distribution identifier.
1679    #[serde(default, skip_serializing_if = "Option::is_none")]
1680    pub dist: Option<Cow<'a, str>>,
1681    /// An optional environment identifier.
1682    #[serde(default, skip_serializing_if = "Option::is_none")]
1683    pub environment: Option<Cow<'a, str>>,
1684    /// Optionally user data to be sent along.
1685    #[serde(default, skip_serializing_if = "Option::is_none")]
1686    pub user: Option<User>,
1687    /// Optionally HTTP request data to be sent along.
1688    #[serde(default, skip_serializing_if = "Option::is_none")]
1689    pub request: Option<Request>,
1690    /// Optional contexts.
1691    #[serde(
1692        default,
1693        skip_serializing_if = "Map::is_empty",
1694        deserialize_with = "deserialize_contexts"
1695    )]
1696    pub contexts: Map<String, Context>,
1697    /// List of breadcrumbs to send along.
1698    #[serde(default, skip_serializing_if = "Values::is_empty")]
1699    pub breadcrumbs: Values<Breadcrumb>,
1700    /// Exceptions to be attached (one or multiple if chained).
1701    #[serde(default, skip_serializing_if = "Values::is_empty")]
1702    pub exception: Values<Exception>,
1703    /// A single stacktrace (deprecated)
1704    #[serde(default, skip_serializing_if = "Option::is_none")]
1705    pub stacktrace: Option<Stacktrace>,
1706    /// Simplified template error location info
1707    #[serde(default, skip_serializing_if = "Option::is_none")]
1708    pub template: Option<TemplateInfo>,
1709    /// A list of threads.
1710    #[serde(default, skip_serializing_if = "Values::is_empty")]
1711    pub threads: Values<Thread>,
1712    /// Optional tags to be attached to the event.
1713    #[serde(default, skip_serializing_if = "Map::is_empty")]
1714    pub tags: Map<String, String>,
1715    /// Optional extra information to be sent with the event.
1716    #[serde(default, skip_serializing_if = "Map::is_empty")]
1717    pub extra: Map<String, Value>,
1718    /// Debug meta information.
1719    #[serde(default, skip_serializing_if = "DebugMeta::is_empty")]
1720    pub debug_meta: Cow<'a, DebugMeta>,
1721    /// SDK metadata
1722    #[serde(default, skip_serializing_if = "Option::is_none")]
1723    pub sdk: Option<Cow<'a, ClientSdkInfo>>,
1724}
1725
1726impl Default for Event<'_> {
1727    fn default() -> Self {
1728        Event {
1729            event_id: event::default_id(),
1730            level: event::default_level(),
1731            fingerprint: event::default_fingerprint(),
1732            culprit: Default::default(),
1733            transaction: Default::default(),
1734            message: Default::default(),
1735            logentry: Default::default(),
1736            logger: Default::default(),
1737            modules: Default::default(),
1738            platform: event::default_platform(),
1739            timestamp: SystemTime::now(),
1740            server_name: Default::default(),
1741            release: Default::default(),
1742            dist: Default::default(),
1743            environment: Default::default(),
1744            user: Default::default(),
1745            request: Default::default(),
1746            contexts: Default::default(),
1747            breadcrumbs: Default::default(),
1748            exception: Default::default(),
1749            stacktrace: Default::default(),
1750            template: Default::default(),
1751            threads: Default::default(),
1752            tags: Default::default(),
1753            extra: Default::default(),
1754            debug_meta: Default::default(),
1755            sdk: Default::default(),
1756        }
1757    }
1758}
1759
1760impl<'a> Event<'a> {
1761    /// Creates a new event with the current timestamp and random id.
1762    pub fn new() -> Event<'a> {
1763        Default::default()
1764    }
1765
1766    /// Creates a fully owned version of the event.
1767    pub fn into_owned(self) -> Event<'static> {
1768        Event {
1769            event_id: self.event_id,
1770            level: self.level,
1771            fingerprint: Cow::Owned(
1772                self.fingerprint
1773                    .iter()
1774                    .map(|x| Cow::Owned(x.to_string()))
1775                    .collect(),
1776            ),
1777            culprit: self.culprit,
1778            transaction: self.transaction,
1779            message: self.message,
1780            logentry: self.logentry,
1781            logger: self.logger,
1782            modules: self.modules,
1783            platform: Cow::Owned(self.platform.into_owned()),
1784            timestamp: self.timestamp,
1785            server_name: self.server_name.map(|x| Cow::Owned(x.into_owned())),
1786            release: self.release.map(|x| Cow::Owned(x.into_owned())),
1787            dist: self.dist.map(|x| Cow::Owned(x.into_owned())),
1788            environment: self.environment.map(|x| Cow::Owned(x.into_owned())),
1789            user: self.user,
1790            request: self.request,
1791            contexts: self.contexts,
1792            breadcrumbs: self.breadcrumbs,
1793            exception: self.exception,
1794            stacktrace: self.stacktrace,
1795            template: self.template,
1796            threads: self.threads,
1797            tags: self.tags,
1798            extra: self.extra,
1799            debug_meta: Cow::Owned(self.debug_meta.into_owned()),
1800            sdk: self.sdk.map(|x| Cow::Owned(x.into_owned())),
1801        }
1802    }
1803}
1804
1805impl fmt::Display for Event<'_> {
1806    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1807        write!(
1808            f,
1809            "Event(id: {}, ts: {})",
1810            self.event_id,
1811            crate::utils::to_rfc3339(&self.timestamp)
1812        )
1813    }
1814}
1815
1816/// Represents a tracing span.
1817#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
1818pub struct Span {
1819    /// The ID of the span
1820    #[serde(default)]
1821    pub span_id: SpanId,
1822    /// Determines which trace the span belongs to.
1823    #[serde(default)]
1824    pub trace_id: TraceId,
1825    /// Determines the parent of this span, if any.
1826    #[serde(default, skip_serializing_if = "Option::is_none")]
1827    pub parent_span_id: Option<SpanId>,
1828    /// Determines whether this span is generated in the same process as its parent, if any.
1829    #[serde(default, skip_serializing_if = "Option::is_none")]
1830    pub same_process_as_parent: Option<bool>,
1831    /// Short code identifying the type of operation the span is measuring.
1832    #[serde(default, skip_serializing_if = "Option::is_none")]
1833    pub op: Option<String>,
1834    /// Longer description of the span's operation, which uniquely identifies the span
1835    /// but is consistent across instances of the span.
1836    #[serde(default, skip_serializing_if = "Option::is_none")]
1837    pub description: Option<String>,
1838    /// The timestamp at the measuring of the span finished.
1839    #[serde(
1840        default,
1841        skip_serializing_if = "Option::is_none",
1842        with = "ts_rfc3339_opt"
1843    )]
1844    pub timestamp: Option<SystemTime>,
1845    /// The timestamp at the measuring of the span started.
1846    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
1847    pub start_timestamp: SystemTime,
1848    /// Describes the status of the span (e.g. `ok`, `cancelled`, etc.)
1849    #[serde(default, skip_serializing_if = "Option::is_none")]
1850    pub status: Option<SpanStatus>,
1851    /// Optional tags to be attached to the span.
1852    #[serde(default, skip_serializing_if = "Map::is_empty")]
1853    pub tags: Map<String, String>,
1854    /// Optional extra information to be sent with the span.
1855    #[serde(default, skip_serializing_if = "Map::is_empty")]
1856    pub data: Map<String, Value>,
1857}
1858
1859impl Default for Span {
1860    fn default() -> Self {
1861        Span {
1862            span_id: Default::default(),
1863            trace_id: Default::default(),
1864            timestamp: Default::default(),
1865            tags: Default::default(),
1866            start_timestamp: SystemTime::now(),
1867            description: Default::default(),
1868            status: Default::default(),
1869            parent_span_id: Default::default(),
1870            same_process_as_parent: Default::default(),
1871            op: Default::default(),
1872            data: Default::default(),
1873        }
1874    }
1875}
1876
1877impl Span {
1878    /// Creates a new span with the current timestamp and random id.
1879    pub fn new() -> Span {
1880        Default::default()
1881    }
1882
1883    /// Finalizes the span with the provided timestamp.
1884    pub fn finish_with_timestamp(&mut self, timestamp: SystemTime) {
1885        self.timestamp = Some(timestamp);
1886    }
1887
1888    /// Finalizes the span.
1889    pub fn finish(&mut self) {
1890        self.timestamp = Some(SystemTime::now());
1891    }
1892}
1893
1894impl fmt::Display for Span {
1895    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1896        write!(
1897            f,
1898            "Span(id: {}, ts: {})",
1899            self.span_id,
1900            crate::utils::to_rfc3339(&self.start_timestamp)
1901        )
1902    }
1903}
1904
1905/// An error used when parsing `SpanStatus`.
1906#[derive(Debug, Error)]
1907#[error("invalid status")]
1908pub struct ParseStatusError;
1909
1910/// The status of a Span.
1911#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)]
1912#[non_exhaustive]
1913pub enum SpanStatus {
1914    /// The operation completed successfully.
1915    #[serde(rename = "ok")]
1916    Ok,
1917    /// Deadline expired before operation could complete.
1918    #[serde(rename = "deadline_exceeded")]
1919    DeadlineExceeded,
1920    /// 401 Unauthorized (actually does mean unauthenticated according to RFC 7235)
1921    #[serde(rename = "unauthenticated")]
1922    Unauthenticated,
1923    /// 403 Forbidden
1924    #[serde(rename = "permission_denied")]
1925    PermissionDenied,
1926    /// 404 Not Found. Some requested entity (file or directory) was not found.
1927    #[serde(rename = "not_found")]
1928    NotFound,
1929    /// 429 Too Many Requests
1930    #[serde(rename = "resource_exhausted")]
1931    ResourceExhausted,
1932    /// Client specified an invalid argument. 4xx.
1933    #[serde(rename = "invalid_argument")]
1934    InvalidArgument,
1935    /// 501 Not Implemented
1936    #[serde(rename = "unimplemented")]
1937    Unimplemented,
1938    /// 503 Service Unavailable
1939    #[serde(rename = "unavailable")]
1940    Unavailable,
1941    /// Other/generic 5xx.
1942    #[serde(rename = "internal_error")]
1943    InternalError,
1944    /// Unknown. Any non-standard HTTP status code.
1945    #[serde(rename = "unknown_error")]
1946    UnknownError,
1947    /// The operation was cancelled (typically by the user).
1948    #[serde(rename = "cancelled")]
1949    Cancelled,
1950    /// Already exists (409)
1951    #[serde(rename = "already_exists")]
1952    AlreadyExists,
1953    /// Operation was rejected because the system is not in a state required for the operation's
1954    #[serde(rename = "failed_precondition")]
1955    FailedPrecondition,
1956    /// The operation was aborted, typically due to a concurrency issue.
1957    #[serde(rename = "aborted")]
1958    Aborted,
1959    /// Operation was attempted past the valid range.
1960    #[serde(rename = "out_of_range")]
1961    OutOfRange,
1962    /// Unrecoverable data loss or corruption
1963    #[serde(rename = "data_loss")]
1964    DataLoss,
1965}
1966
1967impl str::FromStr for SpanStatus {
1968    type Err = ParseStatusError;
1969
1970    fn from_str(s: &str) -> Result<SpanStatus, Self::Err> {
1971        Ok(match s {
1972            "ok" => SpanStatus::Ok,
1973            "deadline_exceeded" => SpanStatus::DeadlineExceeded,
1974            "unauthenticated" => SpanStatus::Unauthenticated,
1975            "permission_denied" => SpanStatus::PermissionDenied,
1976            "not_found" => SpanStatus::NotFound,
1977            "resource_exhausted" => SpanStatus::ResourceExhausted,
1978            "invalid_argument" => SpanStatus::InvalidArgument,
1979            "unimplemented" => SpanStatus::Unimplemented,
1980            "unavailable" => SpanStatus::Unavailable,
1981            "internal_error" => SpanStatus::InternalError,
1982            "unknown_error" => SpanStatus::UnknownError,
1983            "cancelled" => SpanStatus::Cancelled,
1984            "already_exists" => SpanStatus::AlreadyExists,
1985            "failed_precondition" => SpanStatus::FailedPrecondition,
1986            "aborted" => SpanStatus::Aborted,
1987            "out_of_range" => SpanStatus::OutOfRange,
1988            "data_loss" => SpanStatus::DataLoss,
1989            _ => return Err(ParseStatusError),
1990        })
1991    }
1992}
1993
1994impl fmt::Display for SpanStatus {
1995    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1996        match self {
1997            SpanStatus::Ok => write!(f, "ok"),
1998            SpanStatus::DeadlineExceeded => write!(f, "deadline_exceeded"),
1999            SpanStatus::Unauthenticated => write!(f, "unauthenticated"),
2000            SpanStatus::PermissionDenied => write!(f, "permission_denied"),
2001            SpanStatus::NotFound => write!(f, "not_found"),
2002            SpanStatus::ResourceExhausted => write!(f, "resource_exhausted"),
2003            SpanStatus::InvalidArgument => write!(f, "invalid_argument"),
2004            SpanStatus::Unimplemented => write!(f, "unimplemented"),
2005            SpanStatus::Unavailable => write!(f, "unavailable"),
2006            SpanStatus::InternalError => write!(f, "internal_error"),
2007            SpanStatus::UnknownError => write!(f, "unknown_error"),
2008            SpanStatus::Cancelled => write!(f, "cancelled"),
2009            SpanStatus::AlreadyExists => write!(f, "already_exists"),
2010            SpanStatus::FailedPrecondition => write!(f, "failed_precondition"),
2011            SpanStatus::Aborted => write!(f, "aborted"),
2012            SpanStatus::OutOfRange => write!(f, "out_of_range"),
2013            SpanStatus::DataLoss => write!(f, "data_loss"),
2014        }
2015    }
2016}
2017
2018/// Represents a tracing transaction.
2019#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
2020pub struct Transaction<'a> {
2021    /// The ID of the event
2022    #[serde(default = "event::default_id", serialize_with = "event::serialize_id")]
2023    pub event_id: Uuid,
2024    /// The transaction name.
2025    #[serde(
2026        rename = "transaction",
2027        default,
2028        skip_serializing_if = "Option::is_none"
2029    )]
2030    pub name: Option<String>,
2031    /// A release identifier.
2032    #[serde(default, skip_serializing_if = "Option::is_none")]
2033    pub release: Option<Cow<'a, str>>,
2034    /// An optional environment identifier.
2035    #[serde(default, skip_serializing_if = "Option::is_none")]
2036    pub environment: Option<Cow<'a, str>>,
2037    /// Optionally user data to be sent along.
2038    #[serde(default, skip_serializing_if = "Option::is_none")]
2039    pub user: Option<User>,
2040    /// Optional tags to be attached to the event.
2041    #[serde(default, skip_serializing_if = "Map::is_empty")]
2042    pub tags: Map<String, String>,
2043    /// Optional extra information to be sent with the event.
2044    #[serde(default, skip_serializing_if = "Map::is_empty")]
2045    pub extra: Map<String, Value>,
2046    /// SDK metadata
2047    #[serde(default, skip_serializing_if = "Option::is_none")]
2048    pub sdk: Option<Cow<'a, ClientSdkInfo>>,
2049    /// A platform identifier for this event.
2050    #[serde(
2051        default = "event::default_platform",
2052        skip_serializing_if = "event::is_default_platform"
2053    )]
2054    pub platform: Cow<'a, str>,
2055    /// The end time of the transaction.
2056    #[serde(
2057        default,
2058        skip_serializing_if = "Option::is_none",
2059        with = "ts_rfc3339_opt"
2060    )]
2061    pub timestamp: Option<SystemTime>,
2062    /// The start time of the transaction.
2063    #[serde(default = "SystemTime::now", with = "ts_seconds_float")]
2064    pub start_timestamp: SystemTime,
2065    /// The collection of finished spans part of this transaction.
2066    pub spans: Vec<Span>,
2067    /// Optional contexts.
2068    #[serde(
2069        default,
2070        skip_serializing_if = "Map::is_empty",
2071        deserialize_with = "deserialize_contexts"
2072    )]
2073    pub contexts: Map<String, Context>,
2074    /// Optionally HTTP request data to be sent along.
2075    #[serde(default, skip_serializing_if = "Option::is_none")]
2076    pub request: Option<Request>,
2077    /// Optionally the server (or device) name of this event.
2078    #[serde(default, skip_serializing_if = "Option::is_none")]
2079    pub server_name: Option<Cow<'a, str>>,
2080}
2081
2082impl Default for Transaction<'_> {
2083    fn default() -> Self {
2084        Transaction {
2085            event_id: event::default_id(),
2086            name: Default::default(),
2087            user: Default::default(),
2088            tags: Default::default(),
2089            extra: Default::default(),
2090            release: Default::default(),
2091            environment: Default::default(),
2092            sdk: Default::default(),
2093            platform: event::default_platform(),
2094            timestamp: Default::default(),
2095            start_timestamp: SystemTime::now(),
2096            spans: Default::default(),
2097            contexts: Default::default(),
2098            request: Default::default(),
2099            server_name: Default::default(),
2100        }
2101    }
2102}
2103
2104impl<'a> Transaction<'a> {
2105    /// Creates a new span transaction the current timestamp and random id.
2106    pub fn new() -> Transaction<'a> {
2107        Default::default()
2108    }
2109
2110    /// Creates a fully owned version of the transaction.
2111    pub fn into_owned(self) -> Transaction<'static> {
2112        Transaction {
2113            event_id: self.event_id,
2114            name: self.name,
2115            user: self.user,
2116            tags: self.tags,
2117            extra: self.extra,
2118            release: self.release.map(|x| Cow::Owned(x.into_owned())),
2119            environment: self.environment.map(|x| Cow::Owned(x.into_owned())),
2120            sdk: self.sdk.map(|x| Cow::Owned(x.into_owned())),
2121            platform: Cow::Owned(self.platform.into_owned()),
2122            timestamp: self.timestamp,
2123            start_timestamp: self.start_timestamp,
2124            spans: self.spans,
2125            contexts: self.contexts,
2126            request: self.request,
2127            server_name: self.server_name.map(|x| Cow::Owned(x.into_owned())),
2128        }
2129    }
2130
2131    /// Finalizes the transaction to be dispatched.
2132    pub fn finish(&mut self) {
2133        self.timestamp = Some(SystemTime::now());
2134    }
2135
2136    /// Finalizes the transaction to be dispatched with the given end timestamp.
2137    pub fn finish_with_timestamp(&mut self, timestamp: SystemTime) {
2138        self.timestamp = Some(timestamp);
2139    }
2140}
2141
2142impl fmt::Display for Transaction<'_> {
2143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2144        write!(
2145            f,
2146            "Transaction(id: {}, ts: {})",
2147            self.event_id,
2148            crate::utils::to_rfc3339(&self.start_timestamp)
2149        )
2150    }
2151}
2152
2153/// A single [structured log](https://docs.sentry.io/product/explore/logs/).
2154#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
2155pub struct Log {
2156    /// The severity of the log (required).
2157    pub level: LogLevel,
2158    /// The log body/message (required).
2159    pub body: String,
2160    /// The ID of the Trace in which this log happened (required).
2161    #[serde(default, skip_serializing_if = "Option::is_none")]
2162    pub trace_id: Option<TraceId>,
2163    /// The timestamp of the log (required).
2164    #[serde(with = "ts_seconds_float")]
2165    pub timestamp: SystemTime,
2166    /// The severity number of the log.
2167    #[serde(default, skip_serializing_if = "Option::is_none")]
2168    pub severity_number: Option<LogSeverityNumber>,
2169    /// Additional arbitrary attributes attached to the log.
2170    #[serde(default, skip_serializing_if = "Map::is_empty")]
2171    pub attributes: Map<String, LogAttribute>,
2172}
2173
2174/// Indicates the severity of a log, according to the
2175/// OpenTelemetry [`SeverityText`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitytext) spec.
2176#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
2177#[serde(rename_all = "lowercase")]
2178pub enum LogLevel {
2179    /// A fine-grained debugging event.
2180    Trace,
2181    /// A debugging event.
2182    Debug,
2183    /// An informational event. Indicates that an event happened.
2184    Info,
2185    /// A warning event. Not an error but is likely more important than an informational event.
2186    Warn,
2187    /// An error event. Something went wrong.
2188    Error,
2189    /// A fatal error such as application or system crash.
2190    Fatal,
2191}
2192
2193/// A number indicating the severity of a log, according to the OpenTelemetry
2194/// [`SeverityNumber`](https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber) spec.
2195#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
2196pub struct LogSeverityNumber(u8);
2197
2198impl LogSeverityNumber {
2199    /// The minimum severity number.
2200    pub const MIN: u8 = 1;
2201    /// The maximum severity number.
2202    pub const MAX: u8 = 24;
2203}
2204
2205impl TryFrom<u8> for LogSeverityNumber {
2206    type Error = String;
2207
2208    fn try_from(value: u8) -> Result<Self, Self::Error> {
2209        if (LogSeverityNumber::MIN..=LogSeverityNumber::MAX).contains(&value) {
2210            Ok(Self(value))
2211        } else {
2212            Err(format!(
2213                "Log severity number must be between {} and {}",
2214                LogSeverityNumber::MIN,
2215                LogSeverityNumber::MAX
2216            ))
2217        }
2218    }
2219}
2220
2221/// An attribute that can be attached to a log.
2222#[derive(Clone, Debug, PartialEq)]
2223pub struct LogAttribute(pub Value);
2224
2225impl<T> From<T> for LogAttribute
2226where
2227    Value: From<T>,
2228{
2229    fn from(value: T) -> Self {
2230        Self(Value::from(value))
2231    }
2232}
2233
2234impl Serialize for LogAttribute {
2235    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2236    where
2237        S: serde::Serializer,
2238    {
2239        use serde::ser::SerializeStruct;
2240        let mut state = serializer.serialize_struct("LogAttribute", 2)?;
2241
2242        match &self.0 {
2243            Value::String(s) => {
2244                state.serialize_field("value", s.as_str())?;
2245                state.serialize_field("type", "string")?;
2246            }
2247            Value::Number(n) => {
2248                if let Some(i) = n.as_i64() {
2249                    state.serialize_field("value", &i)?;
2250                    state.serialize_field("type", "integer")?;
2251                } else if let Some(u) = n.as_u64() {
2252                    // Converting to a f64 could lead to precision loss
2253                    state.serialize_field("value", &u.to_string())?;
2254                    state.serialize_field("type", "string")?;
2255                } else if let Some(f) = n.as_f64() {
2256                    state.serialize_field("value", &f)?;
2257                    state.serialize_field("type", "double")?;
2258                } else {
2259                    // This should be unreachable, as a `Value::Number` can only be built from an i64, u64 or f64
2260                    state.serialize_field("value", &n.to_string())?;
2261                    state.serialize_field("type", "string")?;
2262                }
2263            }
2264            Value::Bool(b) => {
2265                state.serialize_field("value", &b)?;
2266                state.serialize_field("type", "boolean")?;
2267            }
2268            // For any other type (Null, Array, Object), convert to string with JSON representation
2269            _ => {
2270                state.serialize_field("value", &self.0.to_string())?;
2271                state.serialize_field("type", "string")?;
2272            }
2273        }
2274
2275        state.end()
2276    }
2277}
2278
2279impl<'de> Deserialize<'de> for LogAttribute {
2280    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2281    where
2282        D: serde::Deserializer<'de>,
2283    {
2284        use serde::de::{self, MapAccess, Visitor};
2285        use std::fmt;
2286
2287        struct LogAttributeVisitor;
2288
2289        impl<'de> Visitor<'de> for LogAttributeVisitor {
2290            type Value = LogAttribute;
2291
2292            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2293                formatter.write_str("a LogAttribute with value and type fields")
2294            }
2295
2296            fn visit_map<V>(self, mut map: V) -> Result<LogAttribute, V::Error>
2297            where
2298                V: MapAccess<'de>,
2299            {
2300                let mut value: Option<serde_json::Value> = None;
2301                let mut type_str: Option<String> = None;
2302
2303                while let Some(key) = map.next_key::<String>()? {
2304                    match key.as_str() {
2305                        "value" => {
2306                            if value.is_some() {
2307                                return Err(de::Error::duplicate_field("value"));
2308                            }
2309                            value = Some(map.next_value()?);
2310                        }
2311                        "type" => {
2312                            if type_str.is_some() {
2313                                return Err(de::Error::duplicate_field("type"));
2314                            }
2315                            type_str = Some(map.next_value()?);
2316                        }
2317                        _ => {
2318                            // Ignore unknown fields
2319                            let _: serde_json::Value = map.next_value()?;
2320                        }
2321                    }
2322                }
2323
2324                let value = value.ok_or_else(|| de::Error::missing_field("value"))?;
2325                let type_str = type_str.ok_or_else(|| de::Error::missing_field("type"))?;
2326
2327                match type_str.as_str() {
2328                    "string" => {
2329                        if !value.is_string() {
2330                            return Err(de::Error::custom(
2331                                "type is 'string' but value is not a string",
2332                            ));
2333                        }
2334                    }
2335                    "integer" => {
2336                        if !value.is_i64() {
2337                            return Err(de::Error::custom(
2338                                "type is 'integer' but value is not an integer",
2339                            ));
2340                        }
2341                    }
2342                    "double" => {
2343                        if !value.is_f64() {
2344                            return Err(de::Error::custom(
2345                                "type is 'double' but value is not a double",
2346                            ));
2347                        }
2348                    }
2349                    "boolean" => {
2350                        if !value.is_boolean() {
2351                            return Err(de::Error::custom(
2352                                "type is 'boolean' but value is not a boolean",
2353                            ));
2354                        }
2355                    }
2356                    _ => {
2357                        return Err(de::Error::custom(format!(
2358                        "expected type to be 'string' | 'integer' | 'double' | 'boolean', found {type_str}"
2359                    )))
2360                    }
2361                }
2362
2363                Ok(LogAttribute(value))
2364            }
2365        }
2366
2367        deserializer.deserialize_map(LogAttributeVisitor)
2368    }
2369}
2370
2371/// An ID that identifies an organization in the Sentry backend.
2372#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
2373pub struct OrganizationId(u64);
2374
2375impl From<u64> for OrganizationId {
2376    fn from(value: u64) -> Self {
2377        Self(value)
2378    }
2379}
2380
2381impl std::str::FromStr for OrganizationId {
2382    type Err = std::num::ParseIntError;
2383
2384    fn from_str(s: &str) -> Result<Self, Self::Err> {
2385        s.parse().map(Self)
2386    }
2387}
2388
2389impl std::fmt::Display for OrganizationId {
2390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2391        write!(f, "{}", self.0)
2392    }
2393}
2394
2395/// A random number generated at the start of a trace by the head of trace SDK.
2396#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
2397pub struct SampleRand(f64);
2398
2399/// An error that indicates failure to construct a SampleRand.
2400#[derive(Debug, Error)]
2401pub enum InvalidSampleRandError {
2402    /// Indicates that the given value cannot be converted to a f64 succesfully.
2403    #[error("failed to parse f64: {0}")]
2404    InvalidFloat(#[from] std::num::ParseFloatError),
2405
2406    /// Indicates that the given float is outside of the valid range for a sample rand, that is the
2407    /// half-open interval [0.0, 1.0).
2408    #[error("sample rand value out of admissible interval [0.0, 1.0)")]
2409    OutOfRange,
2410}
2411
2412impl TryFrom<f64> for SampleRand {
2413    type Error = InvalidSampleRandError;
2414
2415    fn try_from(value: f64) -> Result<Self, Self::Error> {
2416        if !(0.0..1.0).contains(&value) {
2417            return Err(InvalidSampleRandError::OutOfRange);
2418        }
2419        Ok(Self(value))
2420    }
2421}
2422
2423impl std::str::FromStr for SampleRand {
2424    type Err = InvalidSampleRandError;
2425
2426    fn from_str(s: &str) -> Result<Self, Self::Err> {
2427        let x: f64 = s.parse().map_err(InvalidSampleRandError::InvalidFloat)?;
2428        Self::try_from(x)
2429    }
2430}
2431
2432impl std::fmt::Display for SampleRand {
2433    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2434        // Special case: "{:.6}" would round values greater than or equal to 0.9999995 to 1.0,
2435        // as Rust uses [rounding half-to-even](https://doc.rust-lang.org/std/fmt/#precision).
2436        // Round to 0.999999 instead to comply with spec.
2437        if self.0 >= 0.9999995 {
2438            write!(f, "0.999999")
2439        } else {
2440            write!(f, "{:.6}", self.0)
2441        }
2442    }
2443}
2444
2445/// The [Dynamic Sampling
2446/// Context](https://develop.sentry.dev/sdk/telemetry/traces/dynamic-sampling-context/).
2447///
2448/// Sentry supports sampling at the server level through [Dynamic Sampling](https://docs.sentry.io/organization/dynamic-sampling/).
2449/// This feature allows users to specify target sample rates for each project via the frontend instead of requiring an application redeployment.
2450/// The backend needs additional information from the SDK to support these features, contained in
2451/// the Dynamic Sampling Context.
2452#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
2453pub struct DynamicSamplingContext {
2454    // Strictly required fields
2455    // Still typed as optional, as when deserializing an envelope created by an older SDK they might still be missing
2456    #[serde(default, skip_serializing_if = "Option::is_none")]
2457    trace_id: Option<TraceId>,
2458    #[serde(default, skip_serializing_if = "Option::is_none")]
2459    public_key: Option<String>,
2460    #[serde(
2461        default,
2462        skip_serializing_if = "Option::is_none",
2463        with = "display_from_str_opt"
2464    )]
2465    sample_rate: Option<f32>,
2466    // Required fields
2467    #[serde(
2468        default,
2469        skip_serializing_if = "Option::is_none",
2470        with = "display_from_str_opt"
2471    )]
2472    sample_rand: Option<SampleRand>,
2473    #[serde(
2474        default,
2475        skip_serializing_if = "Option::is_none",
2476        with = "display_from_str_opt"
2477    )]
2478    sampled: Option<bool>,
2479    #[serde(default, skip_serializing_if = "Option::is_none")]
2480    release: Option<String>,
2481    #[serde(default, skip_serializing_if = "Option::is_none")]
2482    environment: Option<String>,
2483    #[serde(default, skip_serializing_if = "Option::is_none")]
2484    transaction: Option<String>,
2485    #[serde(
2486        default,
2487        skip_serializing_if = "Option::is_none",
2488        with = "display_from_str_opt"
2489    )]
2490    org_id: Option<OrganizationId>,
2491}
2492
2493impl DynamicSamplingContext {
2494    /// Creates an empty Dynamic Sampling Context.
2495    pub fn new() -> Self {
2496        Default::default()
2497    }
2498
2499    /// Sets the trace ID.
2500    #[must_use]
2501    pub fn with_trace_id(mut self, trace_id: TraceId) -> Self {
2502        self.trace_id = Some(trace_id);
2503        self
2504    }
2505
2506    /// Sets the DSN public key.
2507    #[must_use]
2508    pub fn with_public_key(mut self, public_key: String) -> Self {
2509        self.public_key = Some(public_key);
2510        self
2511    }
2512
2513    /// Sets the sample rate.
2514    #[must_use]
2515    pub fn with_sample_rate(mut self, sample_rate: f32) -> Self {
2516        self.sample_rate = Some(sample_rate);
2517        self
2518    }
2519
2520    /// Sets the sample random value generated by the head of trace SDK.
2521    #[must_use]
2522    pub fn with_sample_rand(mut self, sample_rand: SampleRand) -> Self {
2523        self.sample_rand = Some(sample_rand);
2524        self
2525    }
2526
2527    /// Sets the sampled flag.
2528    #[must_use]
2529    pub fn with_sampled(mut self, sampled: bool) -> Self {
2530        self.sampled = Some(sampled);
2531        self
2532    }
2533
2534    /// Sets the release.
2535    #[must_use]
2536    pub fn with_release(mut self, release: String) -> Self {
2537        self.release = Some(release);
2538        self
2539    }
2540
2541    /// Sets the environment.
2542    #[must_use]
2543    pub fn with_environment(mut self, environment: String) -> Self {
2544        self.environment = Some(environment);
2545        self
2546    }
2547
2548    /// Sets the transaction.
2549    #[must_use]
2550    pub fn with_transaction(mut self, transaction: String) -> Self {
2551        self.transaction = Some(transaction);
2552        self
2553    }
2554
2555    /// Sets the organization ID.
2556    #[must_use]
2557    pub fn with_org_id(mut self, org_id: OrganizationId) -> Self {
2558        self.org_id = Some(org_id);
2559        self
2560    }
2561}