use std::{error::Error, fmt::Display};
use serde::{Deserialize, Serialize, Serializer};
use uuid::Uuid;
use crate::crontab_validator;
#[derive(Debug)]
pub struct CrontabParseError {
invalid_crontab: String,
}
impl Display for CrontabParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"\"{}\" is not a valid crontab schedule.\n\t \
For help determining why this schedule is invalid, you can use this site: \
https://crontab.guru/#{}",
self.invalid_crontab,
self.invalid_crontab
.split_whitespace()
.collect::<Vec<_>>()
.join("_"),
)
}
}
impl Error for CrontabParseError {}
impl CrontabParseError {
pub fn new(invalid_crontab: &str) -> Self {
Self {
invalid_crontab: String::from(invalid_crontab),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MonitorCheckInStatus {
Ok,
Error,
InProgress,
Missed,
#[serde(other)]
Unknown,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum MonitorSchedule {
Crontab {
value: String,
},
Interval {
value: u64,
unit: MonitorIntervalUnit,
},
}
impl MonitorSchedule {
pub fn from_crontab(crontab_str: &str) -> Result<Self, CrontabParseError> {
if crontab_validator::validate(crontab_str) {
Ok(Self::Crontab {
value: String::from(crontab_str),
})
} else {
Err(CrontabParseError::new(crontab_str))
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MonitorIntervalUnit {
Year,
Month,
Week,
Day,
Hour,
Minute,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct MonitorConfig {
pub schedule: MonitorSchedule,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub checkin_margin: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_runtime: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub failure_issue_threshold: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recovery_threshold: Option<u64>,
}
fn serialize_id<S: Serializer>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_some(&uuid.as_simple())
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct MonitorCheckIn {
#[serde(serialize_with = "serialize_id")]
pub check_in_id: Uuid,
pub monitor_slug: String,
pub status: MonitorCheckInStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub monitor_config: Option<MonitorConfig>,
}