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")]
16#[derive(Default)]
17pub enum SessionStatus {
18 #[default]
22 Ok,
23 Exited,
25 Crashed,
27 Abnormal,
29}
30
31#[derive(Debug, Error)]
33#[error("invalid session status")]
34pub struct ParseSessionStatusError;
35
36impl str::FromStr for SessionStatus {
37 type Err = ParseSessionStatusError;
38
39 fn from_str(string: &str) -> Result<Self, Self::Err> {
40 Ok(match string {
41 "ok" => SessionStatus::Ok,
42 "crashed" => SessionStatus::Crashed,
43 "abnormal" => SessionStatus::Abnormal,
44 "exited" => SessionStatus::Exited,
45 _ => return Err(ParseSessionStatusError),
46 })
47 }
48}
49
50impl fmt::Display for SessionStatus {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match *self {
53 SessionStatus::Ok => write!(f, "ok"),
54 SessionStatus::Crashed => write!(f, "crashed"),
55 SessionStatus::Abnormal => write!(f, "abnormal"),
56 SessionStatus::Exited => write!(f, "exited"),
57 }
58 }
59}
60
61#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
63pub struct SessionAttributes<'a> {
64 pub release: Cow<'a, str>,
66
67 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub environment: Option<Cow<'a, str>>,
70
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub ip_address: Option<IpAddr>,
74
75 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub user_agent: Option<String>,
78}
79
80fn is_false(val: &bool) -> bool {
81 !val
82}
83
84#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
89pub struct SessionUpdate<'a> {
90 #[serde(rename = "sid", default = "crate::random_uuid")]
92 pub session_id: Uuid,
93
94 #[serde(rename = "did", default)]
96 pub distinct_id: Option<String>,
97
98 #[serde(rename = "seq", default, skip_serializing_if = "Option::is_none")]
100 pub sequence: Option<u64>,
101
102 #[serde(
104 default,
105 skip_serializing_if = "Option::is_none",
106 with = "ts_rfc3339_opt"
107 )]
108 pub timestamp: Option<SystemTime>,
109
110 #[serde(default = "SystemTime::now", with = "ts_rfc3339")]
112 pub started: SystemTime,
113
114 #[serde(default, skip_serializing_if = "is_false")]
116 pub init: bool,
117
118 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub duration: Option<f64>,
121
122 #[serde(default)]
124 pub status: SessionStatus,
125
126 #[serde(default)]
128 pub errors: u64,
129
130 #[serde(rename = "attrs")]
132 pub attributes: SessionAttributes<'a>,
133}
134
135#[allow(clippy::trivially_copy_pass_by_ref)]
136fn is_zero(val: &u32) -> bool {
137 *val == 0
138}
139
140#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
142pub struct SessionAggregateItem {
143 #[serde(with = "ts_rfc3339")]
145 pub started: SystemTime,
146 #[serde(rename = "did", default, skip_serializing_if = "Option::is_none")]
148 pub distinct_id: Option<String>,
149 #[serde(default, skip_serializing_if = "is_zero")]
151 pub exited: u32,
152 #[serde(default, skip_serializing_if = "is_zero")]
154 pub errored: u32,
155 #[serde(default, skip_serializing_if = "is_zero")]
157 pub abnormal: u32,
158 #[serde(default, skip_serializing_if = "is_zero")]
160 pub crashed: u32,
161}
162
163#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
168pub struct SessionAggregates<'a> {
169 #[serde(default)]
171 pub aggregates: Vec<SessionAggregateItem>,
172 #[serde(rename = "attrs")]
174 pub attributes: SessionAttributes<'a>,
175}