use crate::BoxFuture;
use crate::agents::signal::SignalRoute;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use thiserror::Error;
#[derive(Error, Debug, Clone)]
#[non_exhaustive]
pub enum StrategyError {
#[error(
"Invalid transition from {current_state} via {attempted_action}. Valid actions: {valid_actions:?}"
)]
InvalidTransition {
current_state: String,
attempted_action: String,
valid_actions: Vec<String>,
},
#[error("Guard rejected transition: {0}")]
GuardRejected(String),
#[error("No initial state defined")]
NoInitialState,
#[error("Execution failed: {0}")]
Execution(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct FsmStateId(pub String);
impl From<&str> for FsmStateId {
fn from(s: &str) -> Self {
Self(s.to_string())
}
}
impl From<String> for FsmStateId {
fn from(s: String) -> Self {
Self(s)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ActionId(pub String);
impl From<&str> for ActionId {
fn from(s: &str) -> Self {
Self(s.to_string())
}
}
impl From<String> for ActionId {
fn from(s: String) -> Self {
Self(s)
}
}
pub trait StrategySnapshot: Send + Sync {
fn to_value(&self) -> Result<Value, StrategyError>;
}
pub trait ExecutionStrategy: Send + Sync {
fn execute<'a>(
&'a self,
action: &'a str,
input: Value,
) -> BoxFuture<'a, Result<Value, StrategyError>>;
fn tick(&self) -> BoxFuture<'_, Result<Option<Value>, StrategyError>>;
fn snapshot(&self) -> Result<Box<dyn StrategySnapshot>, StrategyError>;
fn signal_routes(&self) -> Vec<SignalRoute> {
Vec::new()
}
}
pub trait GuardCondition: Send + Sync {
fn evaluate(&self, input: &Value) -> bool;
fn name(&self) -> &str;
}
pub struct ClosureGuard {
name: String,
f: Box<dyn Fn(&Value) -> bool + Send + Sync>,
}
impl ClosureGuard {
pub fn new(
name: impl Into<String>,
f: impl Fn(&Value) -> bool + Send + Sync + 'static,
) -> Self {
Self {
name: name.into(),
f: Box::new(f),
}
}
}
impl std::fmt::Debug for ClosureGuard {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ClosureGuard")
.field("name", &self.name)
.finish_non_exhaustive()
}
}
impl GuardCondition for ClosureGuard {
fn evaluate(&self, input: &Value) -> bool {
(self.f)(input)
}
fn name(&self) -> &str {
&self.name
}
}