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}