Skip to main content

platform_core/
health.rs

1use crate::error::AppResult;
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4use std::fmt::Debug;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
8#[serde(rename_all = "snake_case")]
9pub enum HealthStatus {
10    Healthy,
11    Degraded,
12    Unhealthy,
13}
14
15#[derive(Debug, Clone, Deserialize, Serialize)]
16pub struct HealthReport {
17    pub name: String,
18    pub status: HealthStatus,
19    pub message: Option<String>,
20}
21
22#[async_trait]
23pub trait HealthCheck: Debug + Send + Sync {
24    fn name(&self) -> &'static str;
25    async fn check(&self) -> AppResult<HealthReport>;
26}
27
28#[derive(Clone, Default)]
29pub struct HealthRegistry {
30    checks: Arc<Vec<Arc<dyn HealthCheck>>>,
31}
32
33impl Debug for HealthRegistry {
34    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        formatter
36            .debug_struct("HealthRegistry")
37            .field("checks", &self.checks.len())
38            .finish()
39    }
40}
41
42impl HealthRegistry {
43    pub fn new(checks: Vec<Arc<dyn HealthCheck>>) -> Self {
44        Self {
45            checks: Arc::new(checks),
46        }
47    }
48
49    pub fn checks(&self) -> &[Arc<dyn HealthCheck>] {
50        &self.checks
51    }
52
53    pub async fn check_all(&self) -> Vec<HealthReport> {
54        let mut reports = Vec::with_capacity(self.checks.len());
55        for check in self.checks.iter() {
56            match check.check().await {
57                Ok(report) => reports.push(report),
58                Err(error) => reports.push(HealthReport {
59                    name: check.name().to_owned(),
60                    status: HealthStatus::Unhealthy,
61                    message: Some(error.public_message),
62                }),
63            }
64        }
65        reports
66    }
67}