1use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum ActionPriority {
10 Critical,
11 High,
12 Standard,
13 Low,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct StateDelta {
19 pub dimension: String,
21 pub from_value: f64,
23 pub to_value: f64,
25}
26
27pub trait ProposedAction: Send + Sync {
31 fn action_id(&self) -> &str;
33
34 fn agent_id(&self) -> &str;
36
37 fn proposed_at(&self) -> DateTime<Utc>;
39
40 fn state_deltas(&self) -> &[StateDelta];
42
43 fn priority(&self) -> ActionPriority;
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct SimpleAction {
50 pub action_id: String,
51 pub agent_id: String,
52 pub proposed_at: DateTime<Utc>,
53 pub state_deltas: Vec<StateDelta>,
54 pub priority: ActionPriority,
55}
56
57impl ProposedAction for SimpleAction {
58 fn action_id(&self) -> &str {
59 &self.action_id
60 }
61
62 fn agent_id(&self) -> &str {
63 &self.agent_id
64 }
65
66 fn proposed_at(&self) -> DateTime<Utc> {
67 self.proposed_at
68 }
69
70 fn state_deltas(&self) -> &[StateDelta] {
71 &self.state_deltas
72 }
73
74 fn priority(&self) -> ActionPriority {
75 self.priority
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn simple_action_implements_trait() {
85 let action = SimpleAction {
86 action_id: "act-1".into(),
87 agent_id: "agent-1".into(),
88 proposed_at: Utc::now(),
89 state_deltas: vec![StateDelta {
90 dimension: "weight_AAPL".into(),
91 from_value: 0.10,
92 to_value: 0.15,
93 }],
94 priority: ActionPriority::Standard,
95 };
96
97 let dyn_action: &dyn ProposedAction = &action;
98 assert_eq!(dyn_action.action_id(), "act-1");
99 assert_eq!(dyn_action.agent_id(), "agent-1");
100 assert_eq!(dyn_action.state_deltas().len(), 1);
101 assert_eq!(dyn_action.priority(), ActionPriority::Standard);
102 }
103
104 #[test]
105 fn action_priority_serialization() {
106 let json = serde_json::to_string(&ActionPriority::Critical).unwrap();
107 let deserialized: ActionPriority = serde_json::from_str(&json).unwrap();
108 assert_eq!(deserialized, ActionPriority::Critical);
109 }
110
111 #[test]
112 fn state_delta_serialization() {
113 let delta = StateDelta {
114 dimension: "x".into(),
115 from_value: 1.0,
116 to_value: 2.0,
117 };
118 let json = serde_json::to_string(&delta).unwrap();
119 let deserialized: StateDelta = serde_json::from_str(&json).unwrap();
120 assert_eq!(deserialized.dimension, "x");
121 assert!((deserialized.from_value - 1.0).abs() < f64::EPSILON);
122 assert!((deserialized.to_value - 2.0).abs() < f64::EPSILON);
123 }
124}