whisky_common/data/primitives/
constructors.rs1use serde_json::{json, Value};
2
3use crate::{data::PlutusDataJson, WError};
4
5#[derive(Clone, Debug)]
6pub struct Constr<T = ()>
7where
8 T: Clone + PlutusDataJson,
9{
10 pub tag: u64,
11 pub fields: T,
12}
13
14impl<T> Constr<T>
15where
16 T: Clone + PlutusDataJson,
17{
18 pub fn new(tag: u64, fields: T) -> Self {
19 Constr { tag, fields }
20 }
21}
22
23impl<T> PlutusDataJson for Constr<T>
24where
25 T: Clone + PlutusDataJson,
26{
27 fn to_json(&self) -> Value {
28 let fields_json = self.fields.to_constr_field();
29 constr(self.tag, fields_json)
30 }
31
32 fn from_json(value: &Value) -> Result<Self, WError> {
33 let tag = value
34 .get("constructor")
35 .ok_or_else(|| WError::new("Constr::from_json", "missing 'constructor' field"))?
36 .as_u64()
37 .ok_or_else(|| WError::new("Constr::from_json", "invalid 'constructor' value"))?;
38
39 let fields_json = value
40 .get("fields")
41 .ok_or_else(|| WError::new("Constr::from_json", "missing 'fields' field"))?;
42
43 let fields = T::from_constr_field(fields_json)
44 .map_err(WError::add_err_trace("Constr::from_json"))?;
45
46 Ok(Constr { tag, fields })
47 }
48}
49
50impl PlutusDataJson for () {
51 fn to_json(&self) -> Value {
52 json!([])
53 }
54 fn to_constr_field(&self) -> Vec<serde_json::Value> {
55 vec![]
56 }
57 fn from_json(value: &Value) -> Result<Self, WError> {
58 if value.is_array() {
60 let arr = value.as_array().unwrap();
61 if arr.is_empty() {
62 return Ok(());
63 }
64 }
65 Err(WError::new("()::from_json", "expected empty array"))
66 }
67}
68
69pub fn constr<N: Into<Value>, T: Into<Value>>(constructor: N, fields: T) -> Value {
72 json!({ "constructor": constructor.into(), "fields": fields.into() })
73}
74
75pub fn constr0<T: Into<Value>>(fields: T) -> Value {
76 constr(0, fields)
77}
78
79pub fn constr1<T: Into<Value>>(fields: T) -> Value {
80 constr(1, fields)
81}
82
83pub fn constr2<T: Into<Value>>(fields: T) -> Value {
84 constr(2, fields)
85}
86
87pub fn con_str<N: Into<Value>, T: Into<Value>>(constructor: N, fields: T) -> Value {
89 json!({ "constructor": constructor.into(), "fields": fields.into() })
90}
91
92pub fn con_str0<T: Into<Value>>(fields: T) -> Value {
94 con_str(0, fields)
95}
96
97pub fn con_str1<T: Into<Value>>(fields: T) -> Value {
99 con_str(1, fields)
100}
101
102pub fn con_str2<T: Into<Value>>(fields: T) -> Value {
104 con_str(2, fields)
105}
106
107#[repr(transparent)]
111pub struct ConstrFields<T>(pub T);
112
113impl<T: Clone> Clone for ConstrFields<T> {
114 fn clone(&self) -> Self {
115 ConstrFields(self.0.clone())
116 }
117}
118
119#[macro_export]
120macro_rules! impl_constr_fields {
121 ( $( $name:ident )+ ) => {
122 #[allow(non_snake_case)]
125 impl<$($name,)+> PlutusDataJson for Box<($($name,)+)>
126 where
127 ($($name,)+): ::core::fmt::Debug,
128 $($name: PlutusDataJson + Clone,)+
129 {
130 fn to_json(&self) -> Value {
131 json!(self.to_constr_field())
132 }
133
134 fn to_constr_field(&self) -> Vec<Value> {
135 let tuple = &**self;
136 let ($($name,)+) = tuple.clone();
137 vec![$($name.to_json(),)+]
138 }
139
140 fn from_json(value: &Value) -> Result<Self, WError> {
141 let arr = value
142 .as_array()
143 .ok_or_else(|| WError::new("Box<tuple>::from_json", "expected array"))?;
144
145 let mut iter = arr.iter();
146 $(
147 let $name = {
148 let item = iter.next()
149 .ok_or_else(|| WError::new("Box<tuple>::from_json", "not enough elements"))?;
150 $name::from_json(item)
151 .map_err(WError::add_err_trace("Box<tuple>::from_json"))?
152 };
153 )+
154
155 Ok(Box::new(($($name,)+)))
156 }
157 }
158
159 #[allow(non_snake_case)]
162 impl<$($name,)+> PlutusDataJson for Box<ConstrFields<($($name,)+)>>
163 where
164 $($name: PlutusDataJson + Clone,)+
165 {
166 fn to_json(&self) -> Value {
167 json!(self.to_constr_field())
168 }
169
170 fn to_constr_field(&self) -> Vec<Value> {
171 let tuple = &self.0;
172 let ($($name,)+) = tuple.clone();
173 vec![$($name.to_json(),)+]
174 }
175
176 fn from_json(value: &Value) -> Result<Self, WError> {
177 let arr = value
178 .as_array()
179 .ok_or_else(|| WError::new("Box<ConstrFields>::from_json", "expected array"))?;
180
181 let mut iter = arr.iter();
182 $(
183 let $name = {
184 let item = iter.next()
185 .ok_or_else(|| WError::new("Box<ConstrFields>::from_json", "not enough elements"))?;
186 $name::from_json(item)
187 .map_err(WError::add_err_trace("Box<ConstrFields>::from_json"))?
188 };
189 )+
190
191 Ok(Box::new(ConstrFields(($($name,)+))))
192 }
193 }
194
195 #[allow(non_snake_case)]
197 impl<$($name,)+> ::core::fmt::Debug for ConstrFields<($($name,)+)>
198 where
199 $($name: ::core::fmt::Debug,)+
200 {
201 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
202 let tuple = &self.0;
203 let ($($name,)+) = tuple;
204 f.debug_tuple("")
205 $(.field($name))+
206 .finish()
207 }
208 }
209 }
210}
211
212impl_constr_fields!(T1 T2);
214impl_constr_fields!(T1 T2 T3);
215impl_constr_fields!(T1 T2 T3 T4);
216impl_constr_fields!(T1 T2 T3 T4 T5);
217impl_constr_fields!(T1 T2 T3 T4 T5 T6);
218impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7);
219impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8);
220impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9);
221impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10);
222impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11);
223impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12);
224
225impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13);
227impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14);
228impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
229impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16);
230impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17);
231impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18);
232impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19);
233impl_constr_fields!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19 T20);
234
235#[macro_export]
236macro_rules! impl_constr_n {
237 ($($name:ident: $tag:expr),+) => {
238 $(
239 #[derive(Clone, Debug, PartialEq)]
240 pub struct $name<T = ()>
241 where
242 T: Clone + PlutusDataJson,
243 {
244 pub fields: T,
245 }
246
247 impl<T> $name<T>
248 where
249 T: Clone + PlutusDataJson,
250 {
251 pub fn new(fields: T) -> Self {
252 $name { fields }
253 }
254 }
255
256 impl<T> PlutusDataJson for $name<T>
257 where
258 T: Clone + PlutusDataJson,
259 {
260 fn to_json(&self) -> Value {
261 let fields_json = self.fields.to_constr_field();
262 constr($tag, fields_json)
263 }
264
265 fn from_json(value: &Value) -> Result<Self, WError> {
266 let tag = value
267 .get("constructor")
268 .ok_or_else(|| WError::new(concat!(stringify!($name), "::from_json"), "missing 'constructor' field"))?
269 .as_u64()
270 .ok_or_else(|| WError::new(concat!(stringify!($name), "::from_json"), "invalid 'constructor' value"))?;
271
272 if tag != $tag {
273 return Err(WError::new(
274 concat!(stringify!($name), "::from_json"),
275 &format!("expected constructor tag {}, got {}", $tag, tag),
276 ));
277 }
278
279 let fields_json = value
280 .get("fields")
281 .ok_or_else(|| WError::new(concat!(stringify!($name), "::from_json"), "missing 'fields' field"))?;
282
283 let fields = T::from_constr_field(fields_json)
284 .map_err(WError::add_err_trace(concat!(stringify!($name), "::from_json")))?;
285
286 Ok($name { fields })
287 }
288 }
289 )+
290 }
291}
292
293impl_constr_n!(
294 Constr0: 0,
295 Constr1: 1,
296 Constr2: 2,
297 Constr3: 3,
298 Constr4: 4,
299 Constr5: 5,
300 Constr6: 6,
301 Constr7: 7,
302 Constr8: 8,
303 Constr9: 9,
304 Constr10: 10
305);
306
307macro_rules! impl_box_constr {
309 ($($constr:ident),+) => {
310 $(
311 impl<T> PlutusDataJson for Box<$constr<T>>
312 where
313 T: Clone + PlutusDataJson,
314 {
315 fn to_json(&self) -> Value {
316 self.as_ref().to_json()
317 }
318
319 fn to_json_string(&self) -> String {
320 self.to_json().to_string()
321 }
322
323 fn to_constr_field(&self) -> Vec<Value> {
324 vec![self.to_json()]
325 }
326
327 fn from_json(value: &Value) -> Result<Self, WError> {
328 $constr::<T>::from_json(value).map(Box::new)
329 }
330 }
331 )+
332 };
333}
334
335impl_box_constr!(
336 Constr, Constr0, Constr1, Constr2, Constr3, Constr4, Constr5, Constr6, Constr7, Constr8,
337 Constr9, Constr10
338);