Skip to main content

awaken_contract/model/
action.rs

1use 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}