zilliqa_rs/contract/
scilla_value.rs

1use std::{collections::HashMap, hash::Hash};
2
3use serde::Deserialize;
4
5use crate::{
6    core::{BNum, ZilAddress},
7    Error,
8};
9
10#[derive(serde::Serialize, Debug, Clone, Deserialize)]
11#[serde(untagged)]
12pub enum ScillaValue {
13    Primitive(String),
14    Adt(AdtValue),
15    Map(HashMap<String, ScillaValue>),
16    List(Vec<ScillaValue>),
17}
18
19#[derive(serde::Serialize, Debug, Clone, Deserialize)]
20pub struct KeyVal {
21    key: ScillaValue,
22    val: ScillaValue,
23}
24
25#[derive(serde::Serialize, Debug, Clone, Deserialize)]
26pub struct ScillaVariable {
27    pub vname: String,
28    pub r#type: String,
29    pub value: ScillaValue,
30}
31
32impl ScillaVariable {
33    pub fn new(vname: String, r#type: String, value: ScillaValue) -> Self {
34        Self { vname, value, r#type }
35    }
36
37    pub fn new_from_str<T: ToScillaValue>(vname: &str, r#type: &str, value: T) -> Self {
38        Self {
39            vname: vname.to_string(),
40            value: value.to_value(),
41            r#type: r#type.to_string(),
42        }
43    }
44}
45
46#[derive(serde::Serialize, Debug, Clone, Deserialize)]
47pub struct AdtValue {
48    constructor: String,
49    argtypes: Vec<String>,
50    arguments: Vec<ScillaValue>,
51}
52
53// TODO: Set better names for trait functions
54pub trait ToScillaValue {
55    fn to_value(&self) -> ScillaValue;
56    fn scilla_type() -> String;
57}
58
59pub trait TryFromScillaValue: Sized {
60    fn try_from_scilla_value(value: ScillaValue) -> Result<Self, Error>;
61}
62
63// Can't use std TryFrom because it conflicts with std implementation for option, map, etc
64pub trait TryIntoRustType<T>: Sized {
65    fn try_into_rust_type(self) -> Result<T, Error>;
66}
67
68impl<T> TryIntoRustType<T> for ScillaValue
69where
70    T: TryFromScillaValue,
71{
72    fn try_into_rust_type(self) -> Result<T, Error> {
73        T::try_from_scilla_value(self)
74    }
75}
76
77macro_rules! from_scilla_value_for {
78    ($t:ty) => {
79        impl TryFromScillaValue for $t {
80            fn try_from_scilla_value(value: ScillaValue) -> Result<$t, Error> {
81                match value {
82                    ScillaValue::Primitive(s) => s
83                        .parse()
84                        .map_err(|_| Error::FailedToParseScillaValue(s, stringify!($t).to_string())),
85                    _ => Err(Error::FailedToParseScillaValue(
86                        serde_json::to_string(&value)?,
87                        stringify!($t).to_string(),
88                    )),
89                }
90            }
91        }
92    };
93}
94
95macro_rules! to_scilla_value_for {
96    ($t:ty, $scilla_type:expr) => {
97        impl ToScillaValue for $t {
98            fn to_value(&self) -> ScillaValue {
99                ScillaValue::Primitive(self.to_string())
100            }
101
102            fn scilla_type() -> String {
103                $scilla_type.to_string()
104            }
105        }
106    };
107}
108
109to_scilla_value_for!(i32, "Int32");
110to_scilla_value_for!(i64, "Int64");
111to_scilla_value_for!(i128, "Int128");
112to_scilla_value_for!(u32, "Uint32");
113to_scilla_value_for!(u64, "Uint64");
114to_scilla_value_for!(u128, "Uint128");
115to_scilla_value_for!(primitive_types::U256, "Uint256");
116to_scilla_value_for!(String, "String");
117to_scilla_value_for!(&str, "String");
118to_scilla_value_for!(ZilAddress, "ByStr20");
119to_scilla_value_for!(&ZilAddress, "ByStr20");
120to_scilla_value_for!(BNum, "BNum");
121
122from_scilla_value_for!(i32);
123from_scilla_value_for!(i64);
124from_scilla_value_for!(i128);
125from_scilla_value_for!(u32);
126from_scilla_value_for!(u64);
127from_scilla_value_for!(u128);
128from_scilla_value_for!(primitive_types::U256);
129from_scilla_value_for!(String);
130from_scilla_value_for!(ZilAddress);
131from_scilla_value_for!(BNum);
132
133impl<T: ToScillaValue> ToScillaValue for Option<T> {
134    fn to_value(&self) -> ScillaValue {
135        match self {
136            Some(v) => ScillaValue::Adt(AdtValue {
137                constructor: "Some".to_string(),
138                argtypes: vec![T::scilla_type()],
139                arguments: vec![v.to_value()],
140            }),
141            None => ScillaValue::Adt(AdtValue {
142                constructor: "None".to_string(),
143                argtypes: vec![T::scilla_type()],
144                arguments: vec![],
145            }),
146        }
147    }
148
149    fn scilla_type() -> String {
150        format!("Option ({})", T::scilla_type())
151    }
152}
153
154impl<T> TryFromScillaValue for Option<T>
155where
156    T: TryFromScillaValue,
157{
158    fn try_from_scilla_value(value: ScillaValue) -> Result<Self, Error> {
159        let error = Error::FailedToParseScillaValue(serde_json::to_string(&value)?, "Option".to_string());
160        if let ScillaValue::Adt(adt) = &value {
161            match adt.constructor.as_str() {
162                "Some" => return Ok(Some(adt.arguments.get(0).ok_or(error)?.to_owned().try_into_rust_type()?)),
163                "None" => return Ok(None),
164                _ => (),
165            }
166        }
167        Err(error)
168    }
169}
170
171impl ToScillaValue for bool {
172    fn to_value(&self) -> ScillaValue {
173        ScillaValue::Adt(AdtValue {
174            constructor: if *self { "True".to_string() } else { "False".to_string() },
175            argtypes: vec![],
176            arguments: vec![],
177        })
178    }
179
180    fn scilla_type() -> String {
181        "Bool".to_string()
182    }
183}
184
185impl TryFromScillaValue for bool {
186    fn try_from_scilla_value(value: ScillaValue) -> Result<Self, Error> {
187        if let ScillaValue::Adt(adt) = &value {
188            match adt.constructor.as_str() {
189                "True" => return Ok(true),
190                "False" => return Ok(false),
191                _ => (),
192            }
193        }
194        Err(Error::FailedToParseScillaValue(
195            serde_json::to_string(&value)?,
196            "bool".to_string(),
197        ))
198    }
199}
200
201impl<T: ToScillaValue, U: ToScillaValue> ToScillaValue for (T, U) {
202    fn to_value(&self) -> ScillaValue {
203        ScillaValue::Adt(AdtValue {
204            constructor: "Pair".to_string(),
205            argtypes: vec![T::scilla_type(), U::scilla_type()],
206            arguments: vec![self.0.to_value(), self.1.to_value()],
207        })
208    }
209
210    fn scilla_type() -> String {
211        format!("Pair {} {}", T::scilla_type(), U::scilla_type())
212    }
213}
214
215impl<T, U> TryFromScillaValue for (T, U)
216where
217    T: TryFromScillaValue,
218    U: TryFromScillaValue,
219{
220    fn try_from_scilla_value(value: ScillaValue) -> Result<Self, Error> {
221        let error = Error::FailedToParseScillaValue(serde_json::to_string(&value)?, "Pair".to_string());
222        if let ScillaValue::Adt(mut adt) = value {
223            if adt.arguments.len() != 2 {
224                return Err(error);
225            }
226            // Safe to call unwrap because we already checked the size
227            let y = adt.arguments.pop().unwrap().try_into_rust_type()?;
228            let x = adt.arguments.pop().unwrap().try_into_rust_type()?;
229            return Ok((x, y));
230        }
231
232        Err(error)
233    }
234}
235
236impl<K: ToScillaValue, V: ToScillaValue> ToScillaValue for HashMap<K, V> {
237    fn to_value(&self) -> ScillaValue {
238        // FIXME:
239        todo!()
240        // ScillaValue::Map(
241        //     self.iter()
242        //         .map(|(key, value)| KeyVal {
243        //             key: key.to_value(),
244        //             val: value.to_value(),
245        //         })
246        //         .collect(),
247        // )
248    }
249
250    fn scilla_type() -> String {
251        format!("Map {} {}", K::scilla_type(), V::scilla_type())
252    }
253}
254
255impl<K: TryFromScillaValue + std::cmp::Eq + Hash, V: TryFromScillaValue> TryFromScillaValue for HashMap<K, V> {
256    fn try_from_scilla_value(value: ScillaValue) -> Result<Self, Error> {
257        let error = Error::FailedToParseScillaValue(serde_json::to_string(&value)?, "Map".to_string());
258        if let ScillaValue::Map(map) = value {
259            return map
260                .into_iter()
261                .map(|(key, val)| {
262                    Ok((
263                        K::try_from_scilla_value(ScillaValue::Primitive(key))?,
264                        V::try_from_scilla_value(val)?,
265                    ))
266                })
267                .collect::<Result<HashMap<K, V>, Error>>();
268        }
269
270        Err(error)
271    }
272}
273
274impl<T: ToScillaValue> ToScillaValue for [T] {
275    fn to_value(&self) -> ScillaValue {
276        if self.is_empty() {
277            ScillaValue::Adt(AdtValue {
278                constructor: "Nil".to_string(),
279                argtypes: vec![T::scilla_type()],
280                arguments: vec![],
281            })
282        } else {
283            ScillaValue::Adt(AdtValue {
284                constructor: "Cons".to_string(),
285                argtypes: vec![T::scilla_type()],
286                arguments: vec![self[0].to_value(), self[1..].to_value()],
287            })
288        }
289    }
290
291    fn scilla_type() -> String {
292        format!("(List ({}))", T::scilla_type())
293    }
294}
295
296impl<T: ToScillaValue> ToScillaValue for Vec<T> {
297    fn to_value(&self) -> ScillaValue {
298        self[..].to_value()
299    }
300
301    fn scilla_type() -> String {
302        <[T]>::scilla_type()
303    }
304}
305
306impl<T: TryFromScillaValue> TryFromScillaValue for Vec<T> {
307    fn try_from_scilla_value(value: ScillaValue) -> Result<Self, Error> {
308        let error = Error::FailedToParseScillaValue(serde_json::to_string(&value)?, "List".to_string());
309        if let ScillaValue::List(list) = value {
310            return list
311                .into_iter()
312                .map(|f| f.try_into_rust_type())
313                .collect::<Result<Vec<T>, Error>>();
314        }
315
316        Err(error)
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use std::collections::HashMap;
323
324    use serde_json::json;
325
326    use crate::core::ZilAddress;
327
328    use super::ToScillaValue;
329
330    #[test]
331    fn check_scilla_types() {
332        assert_eq!(
333            Vec::<(ZilAddress, Vec<(ZilAddress, u32)>)>::scilla_type(),
334            "(List (Pair ByStr20 (List (Pair ByStr20 Uint32))))"
335        );
336    }
337
338    #[test]
339    fn test_bool_value() {
340        let scilla_value = true.to_value();
341        let scilla_value = serde_json::to_string(&scilla_value).unwrap();
342        assert_eq!(r#"{"constructor":"True","argtypes":[],"arguments":[]}"#, scilla_value);
343        assert_eq!("Bool", bool::scilla_type());
344    }
345
346    #[test]
347    fn test_option_value() {
348        let scilla_value = Some(true).to_value();
349        let scilla_value = serde_json::to_string(&scilla_value).unwrap();
350        assert_eq!(
351            r#"{"constructor":"Some","argtypes":["Bool"],"arguments":[{"constructor":"True","argtypes":[],"arguments":[]}]}"#,
352            scilla_value
353        );
354        assert_eq!("Option (Bool)", Option::<bool>::scilla_type());
355    }
356
357    #[test]
358    fn test_pair_value() {
359        assert_eq!("Pair String Uint32", <(String, u32)>::scilla_type());
360
361        let scilla_value = ("hello".to_string(), 123u32).to_value();
362        let scilla_value = serde_json::to_string(&scilla_value).unwrap();
363        assert_eq!(
364            r#"{"constructor":"Pair","argtypes":["String","Uint32"],"arguments":["hello","123"]}"#,
365            scilla_value
366        );
367    }
368
369    #[test]
370    fn test_map_value() {
371        assert_eq!("Map (String) (Int32)", HashMap::<String, i32>::scilla_type());
372
373        let mut vikings = HashMap::new();
374        vikings.insert("Denmark", 24);
375
376        let json = json!([
377            {
378                "key": "Denmark", "val": "24",
379            },
380        ]);
381        let scilla_value = vikings.to_value();
382        let scilla_value = serde_json::to_string(&scilla_value).unwrap();
383        assert_eq!(serde_json::to_string(&json).unwrap(), scilla_value);
384    }
385
386    #[test]
387    fn test_list_value() {
388        assert_eq!("List (String)", Vec::<String>::scilla_type());
389        let scilla_value = vec!["salam".to_string(), "salam2".to_string()].to_value();
390        let scilla_value = serde_json::to_string(&scilla_value).unwrap();
391        assert_eq!("{\"constructor\":\"Cons\",\"argtypes\":[\"String\"],\"arguments\":[\"salam\",{\"constructor\":\"Cons\",\"argtypes\":[\"String\"],\"arguments\":[\"salam2\",{\"constructor\":\"Nil\",\"argtypes\":[\"String\"],\"arguments\":[]}]}]}", scilla_value);
392    }
393
394    #[test]
395    // FIXME
396    fn test_list_of_pair_value() {
397        assert_eq!("List (Pair String Uint128)", Vec::<(String, u128)>::scilla_type());
398        let scilla_value = vec![("salam".to_string(), 1u128), ("salam2".to_string(), 2u128)].to_value();
399        // let scilla_value = serde_json::to_string(&scilla_value).unwrap();
400        println!("{}", serde_json::to_string_pretty(&scilla_value).unwrap());
401        // assert_eq!("k1", scilla_value);
402        // assert_eq!("{\"constructor\":\"Cons\",\"argtypes\":[\"String\"],\"arguments\":[\"salam\",{\"constructor\":\"Cons\",\"argtypes\":[\"String\"],\"arguments\":[\"salam2\",{\"constructor\":\"Nil\",\"argtypes\":[\"String\"],\"arguments\":[]}]}]}", scilla_value);
403    }
404}