use serde::{Deserialize, Serialize};
use super::{Extra, Timestamp, time};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum Level {
None,
Lowest,
Low,
Info,
Warn,
Urgent,
}
impl Level {
#[must_use]
pub const fn as_i8(self) -> i8 {
match self {
Self::None => -3,
Self::Lowest => -2,
Self::Low => -1,
Self::Info => 0,
Self::Warn => 1,
Self::Urgent => 2,
}
}
#[must_use]
pub const fn from_i64(level: i64) -> Self {
match level {
i64::MIN..=-3 => Self::None,
-2 => Self::Lowest,
-1 => Self::Low,
0 => Self::Info,
1 => Self::Warn,
_ => Self::Urgent,
}
}
}
impl Serialize for Level {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_i8(self.as_i8())
}
}
impl<'de> Deserialize<'de> for Level {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
super::de::int::deserialize(d)?
.map(Self::from_i64)
.ok_or_else(|| serde::de::Error::custom("expected a numeric alarm level"))
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct LoopNotification {
#[serde(rename = "eventType")]
pub event_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
#[serde(
rename = "reasonDisplay",
default,
skip_serializing_if = "Option::is_none"
)]
pub reason_display: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration: Option<f64>,
#[serde(
rename = "remoteCarbs",
default,
skip_serializing_if = "Option::is_none"
)]
pub remote_carbs: Option<f64>,
#[serde(
rename = "remoteAbsorption",
default,
skip_serializing_if = "Option::is_none"
)]
pub remote_absorption: Option<f64>,
#[serde(
rename = "remoteBolus",
default,
skip_serializing_if = "Option::is_none"
)]
pub remote_bolus: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub otp: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(rename = "enteredBy", default, skip_serializing_if = "Option::is_none")]
pub entered_by: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none", with = "time::iso")]
pub created_at: Option<Timestamp>,
#[serde(flatten)]
pub extra: Extra,
}
impl LoopNotification {
#[must_use]
pub fn override_preset(name: impl Into<String>, minutes: Option<f64>) -> Self {
let name = name.into();
Self {
event_type: "Temporary Override".into(),
reason_display: Some(name.clone()),
reason: Some(name),
duration: minutes,
created_at: Some(Timestamp::now()),
..Self::default()
}
}
#[must_use]
pub fn cancel_override() -> Self {
Self {
event_type: "Temporary Override Cancel".into(),
created_at: Some(Timestamp::now()),
..Self::default()
}
}
#[must_use]
pub fn carbs(grams: f64, absorption_hours: f64, otp: impl Into<String>) -> Self {
Self {
event_type: "Remote Carbs Entry".into(),
remote_carbs: Some(grams),
remote_absorption: Some(absorption_hours),
otp: Some(otp.into()),
created_at: Some(Timestamp::now()),
..Self::default()
}
}
#[must_use]
pub fn bolus(units: f64, otp: impl Into<String>) -> Self {
Self {
event_type: "Remote Bolus Entry".into(),
remote_bolus: Some(units),
otp: Some(otp.into()),
created_at: Some(Timestamp::now()),
..Self::default()
}
}
}