use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceRequest {
pub cmd: String,
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub needs: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub http_port: Option<u16>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ServiceState {
pub name: String,
pub status: ServiceStatus,
#[serde(default)]
pub pid: Option<u32>,
#[serde(default)]
pub started_at: Option<DateTime<Utc>>,
#[serde(default)]
pub error: Option<String>,
#[serde(default)]
pub restart_count: Option<u32>,
#[serde(default)]
pub next_restart_at: Option<DateTime<Utc>>,
#[serde(default)]
pub cmd: Option<String>,
#[serde(default)]
pub args: Option<Vec<String>>,
#[serde(default)]
pub needs: Option<Vec<String>>,
#[serde(default)]
pub http_port: Option<u16>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ServiceStatus {
#[default]
Stopped,
Starting,
Running,
Stopping,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Signal {
SIGHUP,
SIGINT,
SIGQUIT,
SIGKILL,
SIGTERM,
SIGUSR1,
SIGUSR2,
}
impl Signal {
pub fn as_i32(&self) -> i32 {
match self {
Signal::SIGHUP => 1,
Signal::SIGINT => 2,
Signal::SIGQUIT => 3,
Signal::SIGKILL => 9,
Signal::SIGUSR1 => 10,
Signal::SIGUSR2 => 12,
Signal::SIGTERM => 15,
}
}
pub fn name(&self) -> &'static str {
match self {
Signal::SIGHUP => "SIGHUP",
Signal::SIGINT => "SIGINT",
Signal::SIGQUIT => "SIGQUIT",
Signal::SIGKILL => "SIGKILL",
Signal::SIGUSR1 => "SIGUSR1",
Signal::SIGUSR2 => "SIGUSR2",
Signal::SIGTERM => "SIGTERM",
}
}
}
impl std::fmt::Display for Signal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
impl Serialize for Signal {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_i32(self.as_i32())
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct ServiceLogEntry {
#[serde(rename = "type")]
pub log_type: String,
#[serde(default)]
pub data: Option<String>,
#[serde(default)]
pub timestamp: Option<DateTime<Utc>>,
}
pub struct ServiceLogStream {
reader: tokio::sync::mpsc::Receiver<ServiceLogEntry>,
_task: tokio::task::JoinHandle<()>,
}
impl ServiceLogStream {
pub(crate) fn new(
reader: tokio::sync::mpsc::Receiver<ServiceLogEntry>,
task: tokio::task::JoinHandle<()>,
) -> Self {
Self {
reader,
_task: task,
}
}
pub async fn next(&mut self) -> Option<ServiceLogEntry> {
self.reader.recv().await
}
pub async fn process_all<F>(&mut self, mut callback: F)
where
F: FnMut(ServiceLogEntry),
{
while let Some(entry) = self.next().await {
callback(entry);
}
}
}
impl ServiceState {
pub fn is_running(&self) -> bool {
self.status == ServiceStatus::Running
}
pub fn is_failed(&self) -> bool {
self.status == ServiceStatus::Failed
}
pub fn is_stopped(&self) -> bool {
self.status == ServiceStatus::Stopped
}
pub fn name(&self) -> &str {
&self.name
}
pub fn error(&self) -> Option<&str> {
self.error.as_deref()
}
}