synwire_core/agents/
execution_strategy.rs1use crate::BoxFuture;
4use crate::agents::signal::SignalRoute;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use thiserror::Error;
8
9#[derive(Error, Debug, Clone)]
11#[non_exhaustive]
12pub enum StrategyError {
13 #[error(
15 "Invalid transition from {current_state} via {attempted_action}. Valid actions: {valid_actions:?}"
16 )]
17 InvalidTransition {
18 current_state: String,
20 attempted_action: String,
22 valid_actions: Vec<String>,
24 },
25
26 #[error("Guard rejected transition: {0}")]
28 GuardRejected(String),
29
30 #[error("No initial state defined")]
32 NoInitialState,
33
34 #[error("Execution failed: {0}")]
36 Execution(String),
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
41pub struct FsmStateId(pub String);
42
43impl From<&str> for FsmStateId {
44 fn from(s: &str) -> Self {
45 Self(s.to_string())
46 }
47}
48
49impl From<String> for FsmStateId {
50 fn from(s: String) -> Self {
51 Self(s)
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
57pub struct ActionId(pub String);
58
59impl From<&str> for ActionId {
60 fn from(s: &str) -> Self {
61 Self(s.to_string())
62 }
63}
64
65impl From<String> for ActionId {
66 fn from(s: String) -> Self {
67 Self(s)
68 }
69}
70
71pub trait StrategySnapshot: Send + Sync {
73 fn to_value(&self) -> Result<Value, StrategyError>;
75}
76
77pub trait ExecutionStrategy: Send + Sync {
81 fn execute<'a>(
83 &'a self,
84 action: &'a str,
85 input: Value,
86 ) -> BoxFuture<'a, Result<Value, StrategyError>>;
87
88 fn tick(&self) -> BoxFuture<'_, Result<Option<Value>, StrategyError>>;
90
91 fn snapshot(&self) -> Result<Box<dyn StrategySnapshot>, StrategyError>;
93
94 fn signal_routes(&self) -> Vec<SignalRoute> {
96 Vec::new()
97 }
98}
99
100pub trait GuardCondition: Send + Sync {
102 fn evaluate(&self, input: &Value) -> bool;
104
105 fn name(&self) -> &str;
107}
108
109pub struct ClosureGuard {
113 name: String,
114 f: Box<dyn Fn(&Value) -> bool + Send + Sync>,
115}
116
117impl ClosureGuard {
118 pub fn new(
120 name: impl Into<String>,
121 f: impl Fn(&Value) -> bool + Send + Sync + 'static,
122 ) -> Self {
123 Self {
124 name: name.into(),
125 f: Box::new(f),
126 }
127 }
128}
129
130impl std::fmt::Debug for ClosureGuard {
131 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132 f.debug_struct("ClosureGuard")
133 .field("name", &self.name)
134 .finish_non_exhaustive()
135 }
136}
137
138impl GuardCondition for ClosureGuard {
139 fn evaluate(&self, input: &Value) -> bool {
140 (self.f)(input)
141 }
142
143 fn name(&self) -> &str {
144 &self.name
145 }
146}