monitrs_core/model/
host.rs1use core::time::Duration;
4use std::time::SystemTime;
5
6use crate::model::{Confidence, MetricState};
7
8#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
17pub enum EnvironmentKind {
18 #[default]
20 NoEvidenceFound,
21 Container,
23 VirtualMachine,
25}
26
27impl EnvironmentKind {
28 #[must_use]
30 pub const fn label(self) -> &'static str {
31 match self {
32 Self::NoEvidenceFound => "no container/VM evidence",
33 Self::Container => "container",
34 Self::VirtualMachine => "virtual machine",
35 }
36 }
37}
38
39#[derive(Clone, Debug, PartialEq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize))]
42pub struct HostEnvironment {
43 pub kind: EnvironmentKind,
45 pub evidence: Box<str>,
49 pub confidence: Confidence,
51}
52
53#[derive(Clone, Debug, PartialEq)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize))]
56pub struct HostSnapshot {
57 pub hostname: MetricState<Box<str>>,
59 pub os_name: MetricState<Box<str>>,
61 pub os_version: MetricState<Box<str>>,
63 pub kernel_version: MetricState<Box<str>>,
65 pub arch: &'static str,
67 pub cpu_brand: MetricState<Box<str>>,
69 pub uptime: MetricState<Duration>,
71 pub boot_time: MetricState<SystemTime>,
73 pub environment: MetricState<HostEnvironment>,
75}
76
77impl HostSnapshot {
78 #[must_use]
80 pub const fn warming_up() -> Self {
81 Self {
82 hostname: MetricState::WarmingUp,
83 os_name: MetricState::WarmingUp,
84 os_version: MetricState::WarmingUp,
85 kernel_version: MetricState::WarmingUp,
86 arch: std::env::consts::ARCH,
87 cpu_brand: MetricState::WarmingUp,
88 uptime: MetricState::WarmingUp,
89 boot_time: MetricState::WarmingUp,
90 environment: MetricState::WarmingUp,
91 }
92 }
93
94 #[must_use]
99 pub fn display_hostname(&self) -> &str {
100 self.hostname
101 .displayable()
102 .map_or("unknown", |(name, _)| name)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn arch_is_known_at_compile_time_and_never_unavailable() {
112 let host = HostSnapshot::warming_up();
113 assert!(!host.arch.is_empty());
114 assert!(
115 [
116 "aarch64",
117 "x86_64",
118 "arm",
119 "x86",
120 "powerpc64",
121 "riscv64",
122 "s390x",
123 "loongarch64"
124 ]
125 .contains(&host.arch),
126 "unexpected arch {}",
127 host.arch
128 );
129 }
130
131 #[test]
132 fn an_unresolved_hostname_renders_as_unknown_not_as_an_empty_title() {
133 let host = HostSnapshot::warming_up();
134 assert_eq!(host.display_hostname(), "unknown");
135 }
136
137 #[test]
138 fn a_stale_hostname_is_still_displayable() {
139 let mut host = HostSnapshot::warming_up();
140 host.hostname = MetricState::Available("dev-mbp".into()).into_stale(Duration::from_secs(5));
141 assert_eq!(host.display_hostname(), "dev-mbp");
142 }
143
144 #[test]
145 fn absence_of_evidence_is_not_reported_as_bare_metal() {
146 assert_eq!(EnvironmentKind::default(), EnvironmentKind::NoEvidenceFound);
147 assert!(
148 EnvironmentKind::NoEvidenceFound
149 .label()
150 .contains("evidence")
151 );
152 }
153
154 #[test]
155 fn an_environment_classification_carries_its_evidence_and_confidence() {
156 let env = HostEnvironment {
157 kind: EnvironmentKind::Container,
158 evidence: "/proc/1/cgroup names docker".into(),
159 confidence: Confidence::High,
160 };
161 assert!(!env.evidence.is_empty());
162 assert_eq!(env.confidence, Confidence::High);
163 }
164}