1use serde::{Deserialize, Serialize};
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
39#[serde(rename_all = "snake_case")]
40pub enum SchemaComponent {
41 DoctorReliability,
43 Status,
45 RepoUpdaterContract,
47 ProcessTriageContract,
49 IncidentLedger,
51 WorkerFacts,
53 ProofIntent,
55 ProofReplayState,
57 WorkerBypassRecord,
59 RedactionPolicy,
61 RemediationView,
63 PlacementPlan,
66 FleetDeployAudit,
69}
70
71#[must_use]
77pub const fn current_version(component: SchemaComponent) -> &'static str {
78 match component {
79 SchemaComponent::DoctorReliability => "1.0.0",
80 SchemaComponent::Status => "1.0.0",
81 SchemaComponent::RepoUpdaterContract => "1.0.0",
82 SchemaComponent::ProcessTriageContract => "1.0.0",
83 SchemaComponent::IncidentLedger => "1.0.0",
84 SchemaComponent::WorkerFacts => "1.0.0",
85 SchemaComponent::ProofIntent => "1.0.0",
86 SchemaComponent::ProofReplayState => "1.0.0",
87 SchemaComponent::WorkerBypassRecord => "1.0.0",
88 SchemaComponent::RedactionPolicy => "1.0.0",
89 SchemaComponent::RemediationView => "1.0.0",
90 SchemaComponent::PlacementPlan => "1.0.0",
91 SchemaComponent::FleetDeployAudit => "1.0.0",
92 }
93}
94
95pub const ALL_COMPONENTS: &[(SchemaComponent, &str)] = &[
98 (
99 SchemaComponent::DoctorReliability,
100 current_version(SchemaComponent::DoctorReliability),
101 ),
102 (
103 SchemaComponent::Status,
104 current_version(SchemaComponent::Status),
105 ),
106 (
107 SchemaComponent::RepoUpdaterContract,
108 current_version(SchemaComponent::RepoUpdaterContract),
109 ),
110 (
111 SchemaComponent::ProcessTriageContract,
112 current_version(SchemaComponent::ProcessTriageContract),
113 ),
114 (
115 SchemaComponent::IncidentLedger,
116 current_version(SchemaComponent::IncidentLedger),
117 ),
118 (
119 SchemaComponent::WorkerFacts,
120 current_version(SchemaComponent::WorkerFacts),
121 ),
122 (
123 SchemaComponent::ProofIntent,
124 current_version(SchemaComponent::ProofIntent),
125 ),
126 (
127 SchemaComponent::ProofReplayState,
128 current_version(SchemaComponent::ProofReplayState),
129 ),
130 (
131 SchemaComponent::WorkerBypassRecord,
132 current_version(SchemaComponent::WorkerBypassRecord),
133 ),
134 (
135 SchemaComponent::RedactionPolicy,
136 current_version(SchemaComponent::RedactionPolicy),
137 ),
138 (
139 SchemaComponent::RemediationView,
140 current_version(SchemaComponent::RemediationView),
141 ),
142 (
143 SchemaComponent::PlacementPlan,
144 current_version(SchemaComponent::PlacementPlan),
145 ),
146 (
147 SchemaComponent::FleetDeployAudit,
148 current_version(SchemaComponent::FleetDeployAudit),
149 ),
150];
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 const PINNED_SNAPSHOT: &[(SchemaComponent, &str)] = &[
164 (SchemaComponent::DoctorReliability, "1.0.0"),
165 (SchemaComponent::Status, "1.0.0"),
166 (SchemaComponent::RepoUpdaterContract, "1.0.0"),
167 (SchemaComponent::ProcessTriageContract, "1.0.0"),
168 (SchemaComponent::IncidentLedger, "1.0.0"),
169 (SchemaComponent::WorkerFacts, "1.0.0"),
170 (SchemaComponent::ProofIntent, "1.0.0"),
171 (SchemaComponent::ProofReplayState, "1.0.0"),
172 (SchemaComponent::WorkerBypassRecord, "1.0.0"),
173 (SchemaComponent::RedactionPolicy, "1.0.0"),
174 (SchemaComponent::RemediationView, "1.0.0"),
175 (SchemaComponent::PlacementPlan, "1.0.0"),
176 (SchemaComponent::FleetDeployAudit, "1.0.0"),
177 ];
178
179 #[test]
180 fn test_schema_versions_match_snapshot() {
181 assert_eq!(
182 ALL_COMPONENTS.len(),
183 PINNED_SNAPSHOT.len(),
184 "Number of components changed without snapshot update. \
185 Either add the missing entry to PINNED_SNAPSHOT or remove the unused entry from ALL_COMPONENTS."
186 );
187 for (i, ((c1, v1), (c2, v2))) in ALL_COMPONENTS
188 .iter()
189 .zip(PINNED_SNAPSHOT.iter())
190 .enumerate()
191 {
192 assert_eq!(
193 (c1, *v1),
194 (c2, *v2),
195 "Mismatch at position {i}: ALL_COMPONENTS has ({c1:?}, {v1}); \
196 PINNED_SNAPSHOT has ({c2:?}, {v2}). \
197 Bump procedure: edit current_version() AND PINNED_SNAPSHOT in the same commit."
198 );
199 }
200 }
201
202 #[test]
203 fn test_current_version_is_const_eval_friendly() {
204 const _DR: &str = current_version(SchemaComponent::DoctorReliability);
207 const _ST: &str = current_version(SchemaComponent::Status);
208 const _RU: &str = current_version(SchemaComponent::RepoUpdaterContract);
209 const _PT: &str = current_version(SchemaComponent::ProcessTriageContract);
210 }
211
212 #[test]
213 fn test_versions_match_semver_shape() {
214 for &(_, version) in ALL_COMPONENTS {
215 let parts: Vec<&str> = version.split('.').collect();
216 assert_eq!(
217 parts.len(),
218 3,
219 "version {version} must be MAJOR.MINOR.PATCH (got {} parts)",
220 parts.len()
221 );
222 for p in parts {
223 assert!(
224 p.chars().all(|ch| ch.is_ascii_digit()),
225 "version {version} contains non-numeric component {p}"
226 );
227 }
228 }
229 }
230
231 #[test]
232 fn test_all_components_unique() {
233 use std::collections::HashSet;
234 let set: HashSet<_> = ALL_COMPONENTS.iter().map(|(c, _)| *c).collect();
235 assert_eq!(
236 set.len(),
237 ALL_COMPONENTS.len(),
238 "duplicate SchemaComponent in ALL_COMPONENTS"
239 );
240 }
241}