use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthStatus {
pub timestamp: u64,
pub level: HealthLevel,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub diagnostics: Option<HashMap<String, String>>,
}
impl HealthStatus {
pub fn healthy() -> Self {
Self {
timestamp: crate::prelude::now_micros(),
level: HealthLevel::Ok,
message: "Operating normally".to_string(),
diagnostics: None,
}
}
pub fn degraded(message: impl Into<String>) -> Self {
Self {
timestamp: crate::prelude::now_micros(),
level: HealthLevel::Degraded,
message: message.into(),
diagnostics: None,
}
}
pub fn unhealthy(message: impl Into<String>) -> Self {
Self {
timestamp: crate::prelude::now_micros(),
level: HealthLevel::Error,
message: message.into(),
diagnostics: None,
}
}
pub fn with_diagnostic(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.diagnostics
.get_or_insert_with(HashMap::new)
.insert(key.into(), value.into());
self
}
pub fn with_message(mut self, message: impl Into<String>) -> Self {
self.message = message.into();
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum HealthLevel {
Ok,
Degraded,
Error,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeStatus {
pub timestamp: u64,
pub name: String,
pub state: NodeState,
pub uptime: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub cpu_usage: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memory_usage: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_rate: Option<f32>,
pub health: HealthStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NodeState {
Starting,
Running,
Paused,
Stopping,
Stopped,
Crashed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemCommand {
#[serde(default)]
pub timestamp: u64,
pub target: String,
pub command: Command,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Command {
Start,
Stop,
Pause,
Resume,
Restart,
HealthCheck,
ReloadConfig,
Shutdown,
}
macro_rules! system_command_ctor {
($(#[$meta:meta])* $name:ident, $variant:expr) => {
$(#[$meta])*
pub fn $name(target: impl Into<String>) -> Self {
Self {
timestamp: crate::prelude::now_micros(),
target: target.into(),
command: $variant,
}
}
};
}
impl SystemCommand {
system_command_ctor!(
start,
Command::Start
);
system_command_ctor!(
stop,
Command::Stop
);
system_command_ctor!(
restart,
Command::Restart
);
system_command_ctor!(
health_check,
Command::HealthCheck
);
}
#[cfg(test)]
mod system_command_tests {
use super::*;
#[test]
fn start_targets_the_given_node_and_tags_the_start_command() {
let cmd = SystemCommand::start("robot-1");
assert_eq!(cmd.target, "robot-1");
assert_eq!(cmd.command, Command::Start);
}
#[test]
fn stop_targets_the_given_node_and_tags_the_stop_command() {
let cmd = SystemCommand::stop("robot-1");
assert_eq!(cmd.target, "robot-1");
assert_eq!(cmd.command, Command::Stop);
}
#[test]
fn restart_targets_the_given_node_and_tags_the_restart_command() {
let cmd = SystemCommand::restart("robot-1");
assert_eq!(cmd.target, "robot-1");
assert_eq!(cmd.command, Command::Restart);
}
#[test]
fn health_check_targets_the_given_node_and_tags_the_health_check_command() {
let cmd = SystemCommand::health_check("robot-1");
assert_eq!(cmd.target, "robot-1");
assert_eq!(cmd.command, Command::HealthCheck);
}
#[test]
fn each_constructor_accepts_any_impl_into_string_target() {
let owned = SystemCommand::start(String::from("robot-2"));
let borrowed = SystemCommand::start("robot-2");
assert_eq!(owned.target, borrowed.target);
}
}