1use sim_kernel::Symbol;
4
5use crate::{ConfigLayer, ProbeMode};
6
7#[derive(Clone, Debug, Default, PartialEq, Eq)]
9pub struct ConfigProbeCaps {
10 pub env: bool,
12 pub os: bool,
14 pub hardware_inventory: bool,
16 pub network_inventory: bool,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct ConfigProbeRequest {
23 pub lib: Symbol,
25 pub mode: ProbeMode,
27 pub caps: ConfigProbeCaps,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
33pub enum ConfigProbeStatus {
34 Applied,
36 Skipped {
38 reason: String,
40 },
41 Denied {
43 capability: String,
45 },
46 Failed {
48 message: String,
50 },
51}
52
53#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct ConfigProbeReport {
56 pub probe: Symbol,
58 pub lib: Symbol,
60 pub mode: ProbeMode,
62 pub status: ConfigProbeStatus,
64 pub emitted_keys: Vec<String>,
66}
67
68pub trait ConfigProbe {
70 fn symbol(&self) -> Symbol;
72
73 fn probe(&self, request: &ConfigProbeRequest) -> (Option<ConfigLayer>, ConfigProbeReport);
75}
76
77#[cfg(test)]
78mod tests {
79 use sim_kernel::Expr;
80
81 use super::*;
82 use crate::{ConfigDir, ConfigSource};
83
84 struct FakeProbe {
85 symbol: Symbol,
86 }
87
88 impl FakeProbe {
89 fn new() -> Self {
90 Self {
91 symbol: Symbol::qualified("config", "fake"),
92 }
93 }
94 }
95
96 impl ConfigProbe for FakeProbe {
97 fn symbol(&self) -> Symbol {
98 self.symbol.clone()
99 }
100
101 fn probe(&self, request: &ConfigProbeRequest) -> (Option<ConfigLayer>, ConfigProbeReport) {
102 let emitted_keys = vec!["backend".to_owned()];
103 let layer = ConfigLayer::new(
104 ConfigSource::Probe {
105 probe: self.symbol(),
106 mode: request.mode,
107 },
108 ConfigDir::one(
109 request.lib.clone(),
110 Expr::Map(vec![(
111 Expr::Symbol(Symbol::new("backend")),
112 Expr::String("modeled".to_owned()),
113 )]),
114 )
115 .unwrap(),
116 );
117 let report = ConfigProbeReport {
118 probe: self.symbol(),
119 lib: request.lib.clone(),
120 mode: request.mode,
121 status: ConfigProbeStatus::Applied,
122 emitted_keys,
123 };
124 (Some(layer), report)
125 }
126 }
127
128 #[test]
129 fn probe_mode_defaults_to_modeled() {
130 assert_eq!(ProbeMode::default(), ProbeMode::Modeled);
131 }
132
133 #[test]
134 fn probe_returns_optional_layer_and_typed_report() {
135 let probe = FakeProbe::new();
136 let request = ConfigProbeRequest {
137 lib: Symbol::qualified("stream", "host"),
138 mode: ProbeMode::default(),
139 caps: ConfigProbeCaps::default(),
140 };
141
142 let (layer, report) = probe.probe(&request);
143
144 assert_eq!(report.probe, Symbol::qualified("config", "fake"));
145 assert_eq!(report.lib, Symbol::qualified("stream", "host"));
146 assert_eq!(report.mode, ProbeMode::Modeled);
147 assert_eq!(report.status, ConfigProbeStatus::Applied);
148 assert_eq!(report.emitted_keys, ["backend"]);
149 assert!(matches!(
150 layer.unwrap().source,
151 ConfigSource::Probe {
152 probe,
153 mode: ProbeMode::Modeled
154 } if probe == Symbol::qualified("config", "fake")
155 ));
156 }
157}