Skip to main content

github_mcp/core/
component_registry.rs

1// GitHub v3 REST API MCP server — generated by mcpify. Do not hand-edit.
2
3use std::collections::HashMap;
4use std::time::SystemTime;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum ComponentStatus {
8    Healthy,
9    Degraded,
10    Unhealthy,
11}
12
13#[derive(Debug, Clone)]
14pub struct ComponentState {
15    pub name: String,
16    pub critical: bool,
17    pub status: ComponentStatus,
18    pub last_checked_at: Option<SystemTime>,
19    pub last_error: Option<String>,
20}
21
22/// Tracks the health of every registered component, distinguishing critical
23/// components (a failure here makes the whole server unhealthy) from
24/// optional ones (a failure only degrades it) — REQ-2.3.4.
25#[derive(Debug, Default)]
26pub struct ComponentRegistry {
27    components: HashMap<String, ComponentState>,
28}
29
30impl ComponentRegistry {
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    pub fn register(&mut self, name: &str, critical: bool) {
36        self.components.insert(
37            name.to_string(),
38            ComponentState {
39                name: name.to_string(),
40                critical,
41                status: ComponentStatus::Healthy,
42                last_checked_at: None,
43                last_error: None,
44            },
45        );
46    }
47
48    pub fn report(&mut self, name: &str, status: ComponentStatus, error: Option<String>) {
49        let critical = self
50            .components
51            .get(name)
52            .map(|existing| existing.critical)
53            .unwrap_or(false);
54        self.components.insert(
55            name.to_string(),
56            ComponentState {
57                name: name.to_string(),
58                critical,
59                status,
60                last_checked_at: Some(SystemTime::now()),
61                last_error: error,
62            },
63        );
64    }
65
66    pub fn overall_status(&self) -> ComponentStatus {
67        let states: Vec<&ComponentState> = self.components.values().collect();
68        if states
69            .iter()
70            .any(|c| c.critical && c.status == ComponentStatus::Unhealthy)
71        {
72            return ComponentStatus::Unhealthy;
73        }
74        if states.iter().any(|c| c.status != ComponentStatus::Healthy) {
75            return ComponentStatus::Degraded;
76        }
77        ComponentStatus::Healthy
78    }
79
80    pub fn snapshot(&self) -> Vec<ComponentState> {
81        self.components.values().cloned().collect()
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn newly_registered_components_start_healthy() {
91        let mut registry = ComponentRegistry::new();
92        registry.register("db", true);
93        assert_eq!(registry.overall_status(), ComponentStatus::Healthy);
94    }
95
96    #[test]
97    fn a_critical_component_failure_makes_the_whole_registry_unhealthy() {
98        let mut registry = ComponentRegistry::new();
99        registry.register("db", true);
100        registry.report(
101            "db",
102            ComponentStatus::Unhealthy,
103            Some("timeout".to_string()),
104        );
105        assert_eq!(registry.overall_status(), ComponentStatus::Unhealthy);
106    }
107
108    #[test]
109    fn a_non_critical_component_failure_only_degrades_the_registry() {
110        let mut registry = ComponentRegistry::new();
111        registry.register("cache", false);
112        registry.report("cache", ComponentStatus::Unhealthy, None);
113        assert_eq!(registry.overall_status(), ComponentStatus::Degraded);
114    }
115}