use std::borrow::Cow;
use std::fmt;
use std::net::IpAddr;
use std::str;
use std::time::SystemTime;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
use crate::utils::{ts_rfc3339, ts_rfc3339_opt};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SessionStatus {
Ok,
Exited,
Crashed,
Abnormal,
}
impl Default for SessionStatus {
fn default() -> Self {
Self::Ok
}
}
#[derive(Debug, Error)]
#[error("invalid session status")]
pub struct ParseSessionStatusError;
impl str::FromStr for SessionStatus {
type Err = ParseSessionStatusError;
fn from_str(string: &str) -> Result<Self, Self::Err> {
Ok(match string {
"ok" => SessionStatus::Ok,
"crashed" => SessionStatus::Crashed,
"abnormal" => SessionStatus::Abnormal,
"exited" => SessionStatus::Exited,
_ => return Err(ParseSessionStatusError),
})
}
}
impl fmt::Display for SessionStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SessionStatus::Ok => write!(f, "ok"),
SessionStatus::Crashed => write!(f, "crashed"),
SessionStatus::Abnormal => write!(f, "abnormal"),
SessionStatus::Exited => write!(f, "exited"),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SessionAttributes<'a> {
pub release: Cow<'a, str>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ip_address: Option<IpAddr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user_agent: Option<String>,
}
fn is_false(val: &bool) -> bool {
!val
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SessionUpdate<'a> {
#[serde(rename = "sid", default = "Uuid::new_v4")]
pub session_id: Uuid,
#[serde(rename = "did", default)]
pub distinct_id: Option<String>,
#[serde(rename = "seq", default, skip_serializing_if = "Option::is_none")]
pub sequence: Option<u64>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "ts_rfc3339_opt"
)]
pub timestamp: Option<SystemTime>,
#[serde(default = "SystemTime::now", with = "ts_rfc3339")]
pub started: SystemTime,
#[serde(default, skip_serializing_if = "is_false")]
pub init: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration: Option<f64>,
#[serde(default)]
pub status: SessionStatus,
#[serde(default)]
pub errors: u64,
#[serde(rename = "attrs")]
pub attributes: SessionAttributes<'a>,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_zero(val: &u32) -> bool {
*val == 0
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SessionAggregateItem {
#[serde(with = "ts_rfc3339")]
pub started: SystemTime,
#[serde(rename = "did", default, skip_serializing_if = "Option::is_none")]
pub distinct_id: Option<String>,
#[serde(default, skip_serializing_if = "is_zero")]
pub exited: u32,
#[serde(default, skip_serializing_if = "is_zero")]
pub errored: u32,
#[serde(default, skip_serializing_if = "is_zero")]
pub abnormal: u32,
#[serde(default, skip_serializing_if = "is_zero")]
pub crashed: u32,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SessionAggregates<'a> {
#[serde(default)]
pub aggregates: Vec<SessionAggregateItem>,
#[serde(rename = "attrs")]
pub attributes: SessionAttributes<'a>,
}