telltale_machine/engine/runtime_state/
policy.rs1pub enum FlowPolicy {
3 AllowAll,
5 DenyAll,
7 AllowRoles(BTreeSet<String>),
9 DenyRoles(BTreeSet<String>),
11 Predicate(Box<dyn FlowPolicyFn>),
14 PredicateExpr(FlowPredicate),
16}
17
18pub trait FlowPolicyFn: Send + Sync {
20 fn eval(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool;
22 fn clone_box(&self) -> Box<dyn FlowPolicyFn>;
24}
25
26impl<F> FlowPolicyFn for F
27where
28 F: Fn(&KnowledgeFact, &str) -> bool + Clone + Send + Sync + 'static,
29{
30 fn eval(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool {
31 self(knowledge, target_role)
32 }
33
34 fn clone_box(&self) -> Box<dyn FlowPolicyFn> {
35 Box::new(self.clone())
36 }
37}
38
39impl Clone for Box<dyn FlowPolicyFn> {
40 fn clone(&self) -> Self {
41 self.clone_box()
42 }
43}
44
45#[allow(clippy::derivable_impls)]
46impl Default for FlowPolicy {
47 fn default() -> Self {
48 Self::AllowAll
49 }
50}
51
52impl Clone for FlowPolicy {
53 fn clone(&self) -> Self {
54 match self {
55 Self::AllowAll => Self::AllowAll,
56 Self::DenyAll => Self::DenyAll,
57 Self::AllowRoles(roles) => Self::AllowRoles(roles.clone()),
58 Self::DenyRoles(roles) => Self::DenyRoles(roles.clone()),
59 Self::Predicate(predicate) => Self::Predicate(predicate.clone()),
60 Self::PredicateExpr(predicate) => Self::PredicateExpr(predicate.clone()),
61 }
62 }
63}
64
65impl fmt::Debug for FlowPolicy {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 match self {
68 Self::AllowAll => f.write_str("AllowAll"),
69 Self::DenyAll => f.write_str("DenyAll"),
70 Self::AllowRoles(roles) => f.debug_tuple("AllowRoles").field(roles).finish(),
71 Self::DenyRoles(roles) => f.debug_tuple("DenyRoles").field(roles).finish(),
72 Self::Predicate(_) => f.write_str("Predicate(<dynamic>)"),
73 Self::PredicateExpr(predicate) => {
74 f.debug_tuple("PredicateExpr").field(predicate).finish()
75 }
76 }
77 }
78}
79
80impl PartialEq for FlowPolicy {
81 fn eq(&self, other: &Self) -> bool {
82 match (self, other) {
83 (Self::AllowAll, Self::AllowAll) => true,
84 (Self::DenyAll, Self::DenyAll) => true,
85 (Self::AllowRoles(lhs), Self::AllowRoles(rhs)) => lhs == rhs,
86 (Self::DenyRoles(lhs), Self::DenyRoles(rhs)) => lhs == rhs,
87 (Self::Predicate(lhs), Self::Predicate(rhs)) => {
88 std::ptr::eq::<dyn FlowPolicyFn>(&**lhs, &**rhs)
92 }
93 (Self::PredicateExpr(lhs), Self::PredicateExpr(rhs)) => lhs == rhs,
94 _ => false,
95 }
96 }
97}
98
99impl Eq for FlowPolicy {}
100
101#[derive(Serialize, Deserialize)]
102enum FlowPolicyRepr {
103 AllowAll,
104 DenyAll,
105 AllowRoles(BTreeSet<String>),
106 DenyRoles(BTreeSet<String>),
107 PredicateExpr(FlowPredicate),
108}
109
110impl Serialize for FlowPolicy {
111 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112 where
113 S: Serializer,
114 {
115 let repr = match self {
116 Self::AllowAll => FlowPolicyRepr::AllowAll,
117 Self::DenyAll => FlowPolicyRepr::DenyAll,
118 Self::AllowRoles(roles) => FlowPolicyRepr::AllowRoles(roles.clone()),
119 Self::DenyRoles(roles) => FlowPolicyRepr::DenyRoles(roles.clone()),
120 Self::PredicateExpr(predicate) => FlowPolicyRepr::PredicateExpr(predicate.clone()),
121 Self::Predicate(_) => {
122 return Err(serde::ser::Error::custom(
123 "runtime closure predicate is not serializable",
124 ))
125 }
126 };
127 repr.serialize(serializer)
128 }
129}
130
131impl<'de> Deserialize<'de> for FlowPolicy {
132 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133 where
134 D: Deserializer<'de>,
135 {
136 let repr = FlowPolicyRepr::deserialize(deserializer)?;
137 let policy = match repr {
138 FlowPolicyRepr::AllowAll => Self::AllowAll,
139 FlowPolicyRepr::DenyAll => Self::DenyAll,
140 FlowPolicyRepr::AllowRoles(roles) => Self::AllowRoles(roles),
141 FlowPolicyRepr::DenyRoles(roles) => Self::DenyRoles(roles),
142 FlowPolicyRepr::PredicateExpr(predicate) => Self::PredicateExpr(predicate),
143 };
144 Ok(policy)
145 }
146}
147
148impl FlowPolicy {
149 #[must_use]
151 pub fn predicate<F>(predicate: F) -> Self
152 where
153 F: Fn(&KnowledgeFact, &str) -> bool + Clone + Send + Sync + 'static,
154 {
155 Self::Predicate(Box::new(predicate))
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
161pub enum FlowPredicate {
162 TargetRolePrefix(String),
164 FactContains(String),
166 EndpointRoleMatchesTarget,
168 All(Vec<FlowPredicate>),
170 Any(Vec<FlowPredicate>),
172}
173
174impl FlowPolicy {
175 #[must_use]
177 pub fn allows(&self, target_role: &str) -> bool {
178 match self {
179 Self::AllowAll => true,
180 Self::DenyAll => false,
181 Self::AllowRoles(roles) => roles.contains(target_role),
182 Self::DenyRoles(roles) => !roles.contains(target_role),
183 Self::Predicate(_) | Self::PredicateExpr(_) => true,
184 }
185 }
186
187 #[must_use]
189 pub fn allows_knowledge(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool {
190 match self {
191 Self::Predicate(predicate) => predicate.eval(knowledge, target_role),
192 Self::PredicateExpr(predicate) => predicate.eval(knowledge, target_role),
193 other => other.allows(target_role),
194 }
195 }
196}
197
198impl FlowPredicate {
199 #[must_use]
201 pub fn eval(&self, knowledge: &KnowledgeFact, target_role: &str) -> bool {
202 match self {
203 Self::TargetRolePrefix(prefix) => target_role.starts_with(prefix),
204 Self::FactContains(fragment) => knowledge.fact.contains(fragment),
205 Self::EndpointRoleMatchesTarget => knowledge.endpoint.role == target_role,
206 Self::All(predicates) => predicates
207 .iter()
208 .all(|predicate| predicate.eval(knowledge, target_role)),
209 Self::Any(predicates) => predicates
210 .iter()
211 .any(|predicate| predicate.eval(knowledge, target_role)),
212 }
213 }
214}
215
216#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
218#[serde(rename_all = "snake_case")]
219pub enum RuntimeTuningProfile {
220 #[default]
222 Standard,
223 M1StressReference,
225}
226
227#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
229#[serde(rename_all = "snake_case")]
230pub enum ThreadedRoundSemantics {
231 #[default]
233 CanonicalOneStep,
234 WaveParallelExtension,
236}
237
238#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
240#[serde(rename_all = "snake_case")]
241pub enum EffectTraceCaptureMode {
242 #[default]
244 Full,
245 TopologyOnly,
247 Disabled,
249}
250
251#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
253#[serde(rename_all = "snake_case")]
254pub enum ObservabilityRetentionMode {
255 #[default]
257 Full,
258 Capped,
260 Disabled,
262}
263
264#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
266pub struct ObservabilityRetentionConfig {
267 #[serde(default)]
269 pub mode: ObservabilityRetentionMode,
270 #[serde(default = "default_observability_retention_capacity")]
272 pub capacity: usize,
273}
274
275const fn default_observability_retention_capacity() -> usize {
276 4_096
277}
278
279impl Default for ObservabilityRetentionConfig {
280 fn default() -> Self {
281 Self {
282 mode: ObservabilityRetentionMode::Full,
283 capacity: default_observability_retention_capacity(),
284 }
285 }
286}
287
288#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
290#[serde(rename_all = "snake_case")]
291pub enum PayloadValidationMode {
292 Off,
294 #[default]
296 Structural,
297 StrictSchema,
299}