use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{
fmt::{Debug, Display},
num::NonZeroU32,
str::FromStr,
};
use url;
pub use url::Url;
pub use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Probe {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub check: Check,
#[serde(skip_serializing_if = "Option::is_none")]
pub interval_secs: Option<NonZeroU32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout_secs: Option<NonZeroU32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retries: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub backoff_secs: Option<u32>,
}
impl Probe {
pub fn new(check: Check) -> Probe {
Probe {
description: None,
check,
interval_secs: None,
timeout_secs: None,
retries: None,
backoff_secs: None,
}
}
}
impl Display for Probe {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.description.as_ref().unwrap_or(&self.check.to_string())
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum Check {
Http(HttpCheck),
}
impl Check {
pub fn http(&self) -> Option<&HttpCheck> {
match self {
Check::Http(http_check) => Some(http_check),
}
}
pub fn http_mut(&mut self) -> Option<&mut HttpCheck> {
match self {
Check::Http(http_check) => Some(http_check),
}
}
}
impl Display for Check {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Check::Http(http_probe) => std::fmt::Display::fmt(http_probe, f),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct TcpProbe {
pub host: String,
pub port: u16,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct HttpCheck {
pub url: Url,
#[serde(skip_serializing_if = "Option::is_none")]
pub ip_protocol: Option<IpProtocol>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tls_insecure_skip_verify: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(inner(range(min = 100, max = 999)))]
pub response_codes: Option<Vec<u16>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_regex: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_agent: Option<String>,
}
impl Display for HttpCheck {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"HTTP GET {} (via {})",
self.url.as_str(),
self.ip_protocol.unwrap_or_default()
)
}
}
impl HttpCheck {
pub fn new(url: Url) -> HttpCheck {
HttpCheck {
url,
ip_protocol: None,
tls_insecure_skip_verify: None,
response_codes: None,
response_regex: None,
user_agent: None,
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub enum IpProtocol {
IPv4,
IPv6,
#[default]
#[serde(rename = "any")]
Any,
}
impl Display for IpProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
IpProtocol::IPv4 => " IPv4",
IpProtocol::IPv6 => " IPv6",
IpProtocol::Any => "IPv4/6",
}
)
}
}
impl FromStr for IpProtocol {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"ipv4" => Ok(IpProtocol::IPv4),
"ipv6" => Ok(IpProtocol::IPv6),
"any" => Ok(IpProtocol::Any),
_ => Err("invalid ip protocol (chose from ipv4, ipv6, any)".to_string()),
}
}
}