1use std::borrow::Cow;
2use std::fmt;
3use std::net::IpAddr;
4use std::str;
5use std::time::SystemTime;
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9use uuid::Uuid;
10
11use crate::utils::{ts_rfc3339, ts_rfc3339_opt};
12
13#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
15#[serde(rename_all = "snake_case")]
16pub enum SessionStatus {
17 Ok,
21 Exited,
23 Crashed,
25 Abnormal,
27}
28
29impl Default for SessionStatus {
30 fn default() -> Self {
31 Self::Ok
32 }
33}
34
35#[derive(Debug, Error)]
37#[error("invalid session status")]
38pub struct ParseSessionStatusError;
39
40impl str::FromStr for SessionStatus {
41 type Err = ParseSessionStatusError;
42
43 fn from_str(string: &str) -> Result<Self, Self::Err> {
44 Ok(match string {
45 "ok" => SessionStatus::Ok,
46 "crashed" => SessionStatus::Crashed,
47 "abnormal" => SessionStatus::Abnormal,
48 "exited" => SessionStatus::Exited,
49 _ => return Err(ParseSessionStatusError),
50 })
51 }
52}
53
54impl fmt::Display for SessionStatus {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match *self {
57 SessionStatus::Ok => write!(f, "ok"),
58 SessionStatus::Crashed => write!(f, "crashed"),
59 SessionStatus::Abnormal => write!(f, "abnormal"),
60 SessionStatus::Exited => write!(f, "exited"),
61 }
62 }
63}
64
65#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
67pub struct SessionAttributes<'a> {
68 pub release: Cow<'a, str>,
70
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub environment: Option<Cow<'a, str>>,
74
75 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub ip_address: Option<IpAddr>,
78
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub user_agent: Option<String>,
82}
83
84fn is_false(val: &bool) -> bool {
85 !val
86}
87
88#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
93pub struct SessionUpdate<'a> {
94 #[serde(rename = "sid", default = "crate::random_uuid")]
96 pub session_id: Uuid,
97
98 #[serde(rename = "did", default)]
100 pub distinct_id: Option<String>,
101
102 #[serde(rename = "seq", default, skip_serializing_if = "Option::is_none")]
104 pub sequence: Option<u64>,
105
106 #[serde(
108 default,
109 skip_serializing_if = "Option::is_none",
110 with = "ts_rfc3339_opt"
111 )]
112 pub timestamp: Option<SystemTime>,
113
114 #[serde(default = "SystemTime::now", with = "ts_rfc3339")]
116 pub started: SystemTime,
117
118 #[serde(default, skip_serializing_if = "is_false")]
120 pub init: bool,
121
122 #[serde(default, skip_serializing_if = "Option::is_none")]
124 pub duration: Option<f64>,
125
126 #[serde(default)]
128 pub status: SessionStatus,
129
130 #[serde(default)]
132 pub errors: u64,
133
134 #[serde(rename = "attrs")]
136 pub attributes: SessionAttributes<'a>,
137}
138
139#[allow(clippy::trivially_copy_pass_by_ref)]
140fn is_zero(val: &u32) -> bool {
141 *val == 0
142}
143
144#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
146pub struct SessionAggregateItem {
147 #[serde(with = "ts_rfc3339")]
149 pub started: SystemTime,
150 #[serde(rename = "did", default, skip_serializing_if = "Option::is_none")]
152 pub distinct_id: Option<String>,
153 #[serde(default, skip_serializing_if = "is_zero")]
155 pub exited: u32,
156 #[serde(default, skip_serializing_if = "is_zero")]
158 pub errored: u32,
159 #[serde(default, skip_serializing_if = "is_zero")]
161 pub abnormal: u32,
162 #[serde(default, skip_serializing_if = "is_zero")]
164 pub crashed: u32,
165}
166
167#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
172pub struct SessionAggregates<'a> {
173 #[serde(default)]
175 pub aggregates: Vec<SessionAggregateItem>,
176 #[serde(rename = "attrs")]
178 pub attributes: SessionAttributes<'a>,
179}