awaken_contract/model/
action.rs1use serde::{Deserialize, Serialize, de::DeserializeOwned};
2
3use crate::error::StateError;
4use crate::state::StateKey;
5
6use super::{JsonValue, Phase, decode_json, encode_json};
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct ScheduledAction {
10 pub phase: Phase,
11 pub key: String,
12 pub payload: JsonValue,
13}
14
15impl ScheduledAction {
16 pub fn new(phase: Phase, key: impl Into<String>, payload: JsonValue) -> Self {
17 Self {
18 phase,
19 key: key.into(),
20 payload,
21 }
22 }
23}
24
25pub trait ScheduledActionSpec: 'static + Send + Sync {
26 const KEY: &'static str;
27 const PHASE: Phase;
28
29 type Payload: Serialize + DeserializeOwned + Send + Sync + 'static;
30
31 fn encode_payload(payload: &Self::Payload) -> Result<JsonValue, StateError> {
32 encode_json(Self::KEY, payload)
33 }
34
35 fn decode_payload(payload: JsonValue) -> Result<Self::Payload, StateError> {
36 decode_json(Self::KEY, payload)
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct ScheduledActionEnvelope {
42 pub id: u64,
43 pub action: ScheduledAction,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct FailedScheduledAction {
48 pub id: u64,
49 pub action: ScheduledAction,
50 pub error: String,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(tag = "op", rename_all = "snake_case")]
55pub enum ScheduledActionQueueUpdate {
56 Push(ScheduledActionEnvelope),
57 Remove { id: u64 },
58}
59
60pub struct PendingScheduledActions;
61
62impl StateKey for PendingScheduledActions {
63 const KEY: &'static str = "__runtime.pending_scheduled_actions";
64
65 type Value = Vec<ScheduledActionEnvelope>;
66 type Update = ScheduledActionQueueUpdate;
67
68 fn apply(value: &mut Self::Value, update: Self::Update) {
69 match update {
70 ScheduledActionQueueUpdate::Push(entry) => value.push(entry),
71 ScheduledActionQueueUpdate::Remove { id } => value.retain(|entry| entry.id != id),
72 }
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(tag = "op", rename_all = "snake_case")]
78pub enum FailedScheduledActionUpdate {
79 Push(FailedScheduledAction),
80 Remove { id: u64 },
81}
82
83pub struct FailedScheduledActions;
84
85impl StateKey for FailedScheduledActions {
86 const KEY: &'static str = "__runtime.failed_scheduled_actions";
87
88 type Value = Vec<FailedScheduledAction>;
89 type Update = FailedScheduledActionUpdate;
90
91 fn apply(value: &mut Self::Value, update: Self::Update) {
92 match update {
93 FailedScheduledActionUpdate::Push(entry) => value.push(entry),
94 FailedScheduledActionUpdate::Remove { id } => value.retain(|entry| entry.id != id),
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 struct TestAction;
104
105 impl ScheduledActionSpec for TestAction {
106 const KEY: &'static str = "test.action";
107 const PHASE: Phase = Phase::BeforeInference;
108 type Payload = String;
109 }
110
111 #[test]
112 fn scheduled_action_spec_round_trip_works() {
113 let payload = "hello".to_string();
114 let encoded = TestAction::encode_payload(&payload).expect("encode should succeed");
115 let decoded = TestAction::decode_payload(encoded).expect("decode should succeed");
116
117 assert_eq!(decoded, payload);
118 }
119}