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
53pub 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
63pub 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 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 todo!()
240 }
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 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 println!("{}", serde_json::to_string_pretty(&scilla_value).unwrap());
401 }
404}