soma_som_ring/
dispatch.rs1#![allow(missing_docs)]
3
4use std::fmt;
17
18#[derive(Debug)]
22#[non_exhaustive]
23pub enum CommandDispatchError {
24 UnknownCommand(String),
26 ExecutionFailed(String),
28 SerializationError(String),
30}
31
32impl fmt::Display for CommandDispatchError {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 match self {
35 Self::UnknownCommand(cmd) => write!(f, "unknown command: {cmd}"),
36 Self::ExecutionFailed(msg) => write!(f, "dispatch failed: {msg}"),
37 Self::SerializationError(msg) => write!(f, "serde error: {msg}"),
38 }
39 }
40}
41
42impl std::error::Error for CommandDispatchError {}
43
44pub trait CommandDispatcher: Send + Sync {
66 fn dispatch(&self, command_type: &str, payload: &[u8])
69 -> Result<Vec<u8>, CommandDispatchError>;
70}
71
72pub trait PolicyProvider: Send + Sync {
102 fn required_permission(&self, command_type: &str) -> Option<String>;
104
105 fn activity_maximum(&self, command_type: &str) -> Option<u8>;
107
108 fn security_floor(&self, command_type: &str) -> Option<u8>;
110
111 fn decision_tier(&self, command_type: &str) -> Option<String>;
113
114 fn role_authorization_level(&self, role: &str) -> Option<u8>;
117}
118
119#[cfg(test)]
123mod tests {
124 use super::*;
125
126 struct EchoDispatcher;
127
128 impl CommandDispatcher for EchoDispatcher {
129 fn dispatch(
130 &self,
131 command_type: &str,
132 payload: &[u8],
133 ) -> Result<Vec<u8>, CommandDispatchError> {
134 if command_type == "unknown" {
135 return Err(CommandDispatchError::UnknownCommand(command_type.into()));
136 }
137 Ok(payload.to_vec())
138 }
139 }
140
141 struct TestPolicyProvider;
142
143 impl PolicyProvider for TestPolicyProvider {
144 fn required_permission(&self, command_type: &str) -> Option<String> {
145 match command_type {
146 "user.create" => Some("users:manage".into()),
147 _ => None,
148 }
149 }
150
151 fn activity_maximum(&self, command_type: &str) -> Option<u8> {
152 match command_type {
153 "user.create" => Some(3),
154 _ => None,
155 }
156 }
157
158 fn security_floor(&self, command_type: &str) -> Option<u8> {
159 match command_type {
160 "user.create" => Some(2),
161 _ => None,
162 }
163 }
164
165 fn decision_tier(&self, command_type: &str) -> Option<String> {
166 match command_type {
167 "user.create" => Some("Significant".into()),
168 _ => None,
169 }
170 }
171
172 fn role_authorization_level(&self, role: &str) -> Option<u8> {
173 match role {
174 "Admin" => Some(4),
175 "Operator" => Some(3),
176 "Viewer" => Some(1),
177 _ => None,
178 }
179 }
180 }
181
182 #[test]
183 fn dispatcher_echo_returns_payload() {
184 let d = EchoDispatcher;
185 let result = d.dispatch("user.create", b"test-payload").unwrap();
186 assert_eq!(result, b"test-payload");
187 }
188
189 #[test]
190 fn dispatcher_unknown_command_errors() {
191 let d = EchoDispatcher;
192 let err = d.dispatch("unknown", b"").unwrap_err();
193 assert!(matches!(err, CommandDispatchError::UnknownCommand(_)));
194 assert!(err.to_string().contains("unknown"));
195 }
196
197 #[test]
198 fn policy_required_permission() {
199 let p = TestPolicyProvider;
200 assert_eq!(
201 p.required_permission("user.create"),
202 Some("users:manage".into())
203 );
204 assert_eq!(p.required_permission("noop"), None);
205 }
206
207 #[test]
208 fn policy_aeq_dimensions() {
209 let p = TestPolicyProvider;
210 assert_eq!(p.activity_maximum("user.create"), Some(3));
211 assert_eq!(p.security_floor("user.create"), Some(2));
212 assert_eq!(p.decision_tier("user.create"), Some("Significant".into()));
213 }
214
215 #[test]
216 fn policy_role_authorization_level() {
217 let p = TestPolicyProvider;
218 assert_eq!(p.role_authorization_level("Admin"), Some(4));
219 assert_eq!(p.role_authorization_level("Viewer"), Some(1));
220 assert_eq!(p.role_authorization_level("Ghost"), None);
221 }
222
223 #[test]
224 fn dispatch_error_display() {
225 let e1 = CommandDispatchError::UnknownCommand("foo".into());
226 let e2 = CommandDispatchError::ExecutionFailed("bar".into());
227 let e3 = CommandDispatchError::SerializationError("baz".into());
228 assert_eq!(e1.to_string(), "unknown command: foo");
229 assert_eq!(e2.to_string(), "dispatch failed: bar");
230 assert_eq!(e3.to_string(), "serde error: baz");
231 }
232}