Skip to main content

awaken_contract/model/
effect.rs

1use serde::{Deserialize, Serialize, de::DeserializeOwned};
2
3use crate::error::StateError;
4
5use super::{JsonValue, decode_json, encode_json};
6
7pub trait EffectSpec: 'static + Send + Sync {
8    const KEY: &'static str;
9
10    type Payload: Serialize + DeserializeOwned + Send + Sync + 'static;
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct TypedEffect {
15    pub key: String,
16    pub payload: JsonValue,
17}
18
19impl TypedEffect {
20    pub fn from_spec<E: EffectSpec>(payload: &E::Payload) -> Result<Self, StateError> {
21        Ok(Self {
22            key: E::KEY.to_string(),
23            payload: encode_json(E::KEY, payload)?,
24        })
25    }
26
27    pub fn decode<E: EffectSpec>(&self) -> Result<E::Payload, StateError> {
28        decode_json(E::KEY, self.payload.clone())
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
37    struct TestPayload {
38        value: String,
39    }
40
41    struct TestEffect;
42    impl EffectSpec for TestEffect {
43        const KEY: &'static str = "test.effect";
44        type Payload = TestPayload;
45    }
46
47    #[test]
48    fn typed_effect_round_trip_works() {
49        let payload = TestPayload {
50            value: "hello".into(),
51        };
52        let encoded =
53            TypedEffect::from_spec::<TestEffect>(&payload).expect("encoding should succeed");
54        let decoded = encoded
55            .decode::<TestEffect>()
56            .expect("decoding should succeed");
57        assert_eq!(decoded, payload);
58    }
59}