1#[derive(Clone, Copy)]
14pub struct LawGate {
15 pub name: &'static str,
17 pub check: fn(&crate::ServerRegistry) -> bool,
19}
20
21impl std::fmt::Debug for LawGate {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.debug_struct("LawGate").field("name", &self.name).finish()
24 }
25}
26
27impl LawGate {
28 pub fn eval(&self, registry: &crate::ServerRegistry) -> bool {
30 (self.check)(registry)
31 }
32}
33
34pub fn accept_gates(registry: &crate::ServerRegistry, gates: &[LawGate]) -> bool {
39 gates.iter().all(|g| g.eval(registry))
40}
41
42pub const DEFAULT_GATES: &[LawGate] = &[
48 LawGate {
49 name: "not-uninitialized",
50 check: |r| r.current_state != crate::service::State::Uninitialized,
51 },
52 LawGate {
53 name: "receipt-present",
54 check: |r| !r.receipts.is_empty(),
55 },
56 LawGate {
61 name: "clean-slate-requires-receipt",
62 check: |r| !r.diagnostics.is_empty() || !r.receipts.is_empty(),
63 },
64];
65
66pub fn run_gate_logic(
75 gate_id: &str,
76 current_state: crate::service::State,
77 root_path: std::path::PathBuf,
78) -> bool {
79 match gate_id {
80 "some-gate" => true,
81 "gate-state-check" => current_state != crate::service::State::Uninitialized,
82 "gate-receipt-check" | "gate-auth-check" => {
83 let spec = crate::diagnostics::law_table::law_table()
84 .iter()
85 .find(|s| s.gate_id == gate_id);
86 let Some(spec) = spec else { return false };
87 let path = root_path.join(spec.receipt_file);
88 if path.exists() {
89 std::fs::read_to_string(&path)
90 .map(|c| c.trim() == spec.receipt_token)
91 .unwrap_or(false)
92 } else {
93 false
94 }
95 }
96 "gate-powl-conformance" => {
97 let spec = crate::diagnostics::law_table::law_table()
100 .iter()
101 .find(|s| s.gate_id == gate_id);
102 match spec {
103 Some(spec) => root_path.join(spec.receipt_file).exists(),
104 None => false, }
106 }
107 _ => {
108 let output = std::process::Command::new("cargo")
109 .arg("check")
110 .current_dir(root_path)
111 .output();
112 match output {
113 Ok(out) => {
114 if !out.status.success() {
115 eprintln!("cargo check failed!");
116 eprintln!("stdout: {}", String::from_utf8_lossy(&out.stdout));
117 eprintln!("stderr: {}", String::from_utf8_lossy(&out.stderr));
118 }
119 out.status.success()
120 }
121 Err(e) => {
122 eprintln!("failed to execute cargo check: {:?}", e);
123 false
124 }
125 }
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::service::State;
134 use std::collections::HashMap;
135
136 fn minimal_registry(state: State) -> crate::ServerRegistry {
137 crate::ServerRegistry {
138 client_capabilities: None,
139 server_capabilities: None,
140 diagnostics: HashMap::new(),
141 repair_plans: HashMap::new(),
142 gates: HashMap::new(),
143 receipts: HashMap::new(),
144 snapshots: HashMap::new(),
145 cleared_diagnostics: std::collections::HashSet::new(),
146 current_state: state,
147 document_versions: HashMap::new(),
148 root_path: std::path::PathBuf::from("/tmp"),
149 action_seq: 0,
150 conformance_delta_log: std::collections::VecDeque::new(),
151 }
152 }
153
154 #[test]
155 fn law_gate_eval_returns_true_when_predicate_passes() {
156 let gate = LawGate {
157 name: "always-true",
158 check: |_| true,
159 };
160 let reg = minimal_registry(State::Initialized);
161 assert!(gate.eval(®));
162 }
163
164 #[test]
165 fn law_gate_eval_returns_false_when_predicate_fails() {
166 let gate = LawGate {
167 name: "always-false",
168 check: |_| false,
169 };
170 let reg = minimal_registry(State::Initialized);
171 assert!(!gate.eval(®));
172 }
173
174 #[test]
175 fn law_gate_debug_shows_name() {
176 let gate = LawGate {
177 name: "my-gate",
178 check: |_| true,
179 };
180 assert!(format!("{gate:?}").contains("my-gate"));
181 }
182
183 #[test]
184 fn accept_gates_empty_slice_always_passes() {
185 let reg = minimal_registry(State::Uninitialized);
186 assert!(accept_gates(®, &[]));
187 }
188
189 #[test]
190 fn accept_gates_all_pass() {
191 let gates = [
192 LawGate {
193 name: "g1",
194 check: |_| true,
195 },
196 LawGate {
197 name: "g2",
198 check: |_| true,
199 },
200 ];
201 let reg = minimal_registry(State::Initialized);
202 assert!(accept_gates(®, &gates));
203 }
204
205 #[test]
206 fn accept_gates_fails_on_first_false_gate() {
207 let gates = [
208 LawGate {
209 name: "fail",
210 check: |_| false,
211 },
212 LawGate {
213 name: "pass",
214 check: |_| true,
215 },
216 ];
217 let reg = minimal_registry(State::Initialized);
218 assert!(!accept_gates(®, &gates));
219 }
220
221 #[test]
222 fn default_gates_block_uninitialized_state() {
223 let reg = minimal_registry(State::Uninitialized);
224 assert!(!accept_gates(®, DEFAULT_GATES));
225 }
226
227 #[test]
228 fn run_gate_logic_some_gate_always_returns_true() {
229 let result = run_gate_logic(
230 "some-gate",
231 State::Uninitialized,
232 std::path::PathBuf::from("/tmp"),
233 );
234 assert!(result);
235 }
236
237 #[test]
238 fn run_gate_logic_state_check_passes_when_initialized() {
239 let result = run_gate_logic(
240 "gate-state-check",
241 State::Initialized,
242 std::path::PathBuf::from("/tmp"),
243 );
244 assert!(result);
245 }
246
247 #[test]
248 fn run_gate_logic_state_check_fails_when_uninitialized() {
249 let result = run_gate_logic(
250 "gate-state-check",
251 State::Uninitialized,
252 std::path::PathBuf::from("/tmp"),
253 );
254 assert!(!result);
255 }
256}