Skip to main content

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