1use crate::categories::{BinaryCategories, TextCategories};
2use parity_scale_codec::{Compact, Decode, Encode, Input, Output};
3use scale_info::prelude::{boxed::Box, vec::Vec};
4
5#[cfg(feature = "std")]
6use serde::{Deserialize, Serialize};
7
8#[cfg(not(feature = "std"))]
12type String = Vec<u8>;
13
14#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
17#[derive(Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
18pub struct Limits {
19 pub min: i64,
21 pub max: i64,
23 pub scale: u32,
28}
29
30fn wrap_to_u64(x: i64) -> u64 {
31 (x as u64).wrapping_add(u64::MAX / 2 + 1)
32}
33
34fn to_i64(x: u64) -> i64 {
35 ((x as i64) ^ (1 << 63)) & (1 << 63) | (x & (u64::MAX >> 1)) as i64
36}
37
38impl Encode for Limits {
39 fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
40 Compact(wrap_to_u64(self.min)).encode_to(dest);
41 Compact(wrap_to_u64(self.max)).encode_to(dest);
42 Compact(self.scale).encode_to(dest);
43 }
44}
45
46impl Decode for Limits {
47 fn decode<I: Input>(input: &mut I) -> Result<Self, parity_scale_codec::Error> {
48 Ok(Self {
49 min: to_i64(Compact::<u64>::decode(input)?.into()),
50 max: to_i64(Compact::<u64>::decode(input)?.into()),
51 scale: Compact::<u32>::decode(input)?.into(),
52 })
53 }
54}
55
56#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
62#[derive(Encode, Decode, Copy, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
63pub enum CodeType {
64 Shards,
66 Wire {
68 looped: Option<bool>,
69 pure: Option<bool>,
70 },
71}
72
73#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
78#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
79pub struct CodeInfo {
80 pub kind: CodeType,
82 pub requires: Vec<(String, VariableType)>,
85 pub exposes: Vec<(String, VariableType)>,
88 pub inputs: Vec<VariableType>,
90 pub output: VariableType,
92}
93
94#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
95#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
96pub struct TableInfo {
97 pub keys: Vec<String>,
99 pub types: Vec<Vec<VariableType>>,
101}
102
103#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
105#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
106pub enum VariableType {
107 None,
109 Any,
111 Bool,
113 Color,
115 Bytes(Option<BinaryCategories>),
117 String(Option<TextCategories>),
119 Image,
121 Audio,
123 Mesh,
125
126 Enum {
128 #[codec(compact)]
129 vendor_id: u32,
130 #[codec(compact)]
131 type_id: u32,
132 },
133
134 Int(Option<Limits>),
136 Int2([Option<Limits>; 2]),
138 Int3([Option<Limits>; 3]),
140 Int4([Option<Limits>; 4]),
142 Int8([Option<Limits>; 8]),
144 Int16([Option<Limits>; 16]),
146
147 Float(Option<Limits>),
149 Float2([Option<Limits>; 2]),
151 Float3([Option<Limits>; 3]),
153 Float4([Option<Limits>; 4]),
155
156 Seq {
158 types: Vec<VariableType>,
159 length_limits: Option<Limits>,
160 },
161
162 Table(TableInfo),
164
165 Object {
167 #[codec(compact)]
168 vendor_id: u32,
169 #[codec(compact)]
170 type_id: u32,
171 },
172
173 Code(Box<CodeInfo>),
175 Channel(Box<VariableType>),
177 Event(Box<VariableType>),
179}
180
181#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
183#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
184pub struct VariableTypeInfo {
185 #[cfg_attr(feature = "std", serde(alias = "type"))]
187 pub type_: VariableType,
188 pub default: Option<Vec<u8>>,
190}
191
192#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
193#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
194pub struct Record {
195 pub name: String,
196 pub types: Vec<VariableTypeInfo>,
197}
198
199impl From<(String, Vec<VariableTypeInfo>)> for Record {
200 fn from((name, types): (String, Vec<VariableTypeInfo>)) -> Self {
201 Self { name, types }
202 }
203}
204
205#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
207#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
208pub struct Trait {
209 pub name: String,
211 pub records: Vec<Record>,
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use crate::categories::TextCategories;
219
220 #[test]
221 fn encode_decode_simple_1() {
222 let mut trait1: Vec<Record> = vec![(
223 "int1".to_string(),
224 vec![VariableTypeInfo {
225 type_: VariableType::Int(None),
226 default: None,
227 }],
228 )
229 .into()];
230
231 trait1 = trait1
233 .into_iter()
234 .map(|r| (r.name.to_lowercase(), r.types).into())
235 .collect();
236 trait1.dedup_by(|a, b| a.name == b.name);
237 trait1.sort_by(|a, b| a.name.cmp(&b.name));
239
240 let trait1 = Trait {
241 name: "Trait1".to_string(),
242 records: trait1,
243 };
244
245 let e_trait1 = trait1.encode();
246
247 let d_trait1 = Trait::decode(&mut e_trait1.as_slice()).unwrap();
248
249 assert!(trait1 == d_trait1);
250 }
251
252 #[test]
253 fn encode_decode_boxed_1() {
254 let mut trait1: Vec<Record> = vec![
255 (
256 "int1".to_string(),
257 vec![VariableTypeInfo {
258 type_: VariableType::Int(None),
259 default: None,
260 }],
261 )
262 .into(),
263 (
264 "boxed1".to_string(),
265 vec![VariableTypeInfo {
266 type_: VariableType::Code(Box::new(CodeInfo {
267 kind: CodeType::Wire {
268 looped: None,
269 pure: None,
270 },
271 requires: vec![("int1".to_string(), VariableType::Int(None))],
272 exposes: vec![],
273 inputs: vec![],
274 output: VariableType::None,
275 })),
276 default: None,
277 }],
278 )
279 .into(),
280 ];
281
282 trait1 = trait1
284 .into_iter()
285 .map(|r| (r.name.to_lowercase(), r.types).into())
286 .collect();
287 trait1.dedup_by(|a, b| a.name == b.name);
288 trait1.sort_by(|a, b| a.name.cmp(&b.name));
289
290 let trait1 = Trait {
291 name: "Trait1".to_string(),
292 records: trait1,
293 };
294
295 let e_trait1 = trait1.encode();
296
297 let d_trait1 = Trait::decode(&mut e_trait1.as_slice()).unwrap();
298
299 assert!(trait1 == d_trait1);
300 assert!(d_trait1.records[0].name == "boxed1".to_string());
301 let type_ = &d_trait1.records[0].types[0].type_;
302 let requires = match type_ {
303 VariableType::Code(code) => &code.requires,
304 _ => panic!("Should be a code"),
305 };
306 assert!(requires[0].0 == "int1".to_string());
307 }
308
309 #[test]
310 fn test_json_simple_1() {
311 let mut trait1: Vec<Record> = vec![(
312 "int1".to_string(),
313 vec![VariableTypeInfo {
314 type_: VariableType::Int(None),
315 default: None,
316 }],
317 )
318 .into()];
319
320 trait1 = trait1
322 .into_iter()
323 .map(|r| (r.name.to_lowercase(), r.types).into())
324 .collect();
325 trait1.dedup_by(|a, b| a.name == b.name);
326 trait1.sort_by(|a, b| a.name.cmp(&b.name));
327
328 let trait1 = Trait {
329 name: "Trait1".to_string(),
330 records: trait1,
331 };
332
333 let e_trait1 = serde_json::to_string(&trait1).unwrap();
334
335 let d_trait1: Trait = serde_json::from_str(&e_trait1).unwrap();
336
337 assert!(trait1 == d_trait1);
338 }
339
340 #[test]
341 fn test_json_boxed_1() {
342 let mut trait1: Vec<Record> = vec![
343 (
344 "int1".to_string(),
345 vec![VariableTypeInfo {
346 type_: VariableType::Int(None),
347 default: None,
348 }],
349 )
350 .into(),
351 (
352 "boxed1".to_string(),
353 vec![VariableTypeInfo {
354 type_: VariableType::Code(Box::new(CodeInfo {
355 kind: CodeType::Wire {
356 looped: None,
357 pure: None,
358 },
359 requires: vec![("int1".to_string(), VariableType::Int(None))],
360 exposes: vec![],
361 inputs: vec![],
362 output: VariableType::None,
363 })),
364 default: None,
365 }],
366 )
367 .into(),
368 ];
369
370 trait1 = trait1
372 .into_iter()
373 .map(|r| (r.name.to_lowercase(), r.types).into())
374 .collect();
375 trait1.dedup_by(|a, b| a.name == b.name);
376 trait1.sort_by(|a, b| a.name.cmp(&b.name));
377
378 let trait1 = Trait {
379 name: "Trait1".to_string(),
380 records: trait1,
381 };
382
383 let e_trait1 = serde_json::to_string(&trait1).unwrap();
384
385 let d_trait1: Trait = serde_json::from_str(&e_trait1).unwrap();
386
387 assert!(trait1 == d_trait1);
388 assert!(d_trait1.records[0].name == "boxed1".to_string());
389 let type_ = &d_trait1.records[0].types[0].type_;
390 let requires = match type_ {
391 VariableType::Code(code) => &code.requires,
392 _ => panic!("Should be a code"),
393 };
394 assert!(requires[0].0 == "int1".to_string());
395 }
396
397 #[test]
398 fn test_json_textual_from_str() {
399 let trait1 = Trait {
400 name: "Trait1".to_string(),
401 records: vec![(
402 "int1".to_string(),
403 vec![VariableTypeInfo {
404 type_: VariableType::Int(None),
405 default: None,
406 }],
407 )
408 .into()],
409 };
410
411 let json_trait1 = r#"{
412 "name": "Trait1",
413 "records": [
414 {
415 "name": "int1",
416 "types": [
417 {
418 "type": {"Int": null},
419 "default": null
420 }
421 ]
422 }
423 ]
424 }"#;
425
426 let d_trait1 = serde_json::from_str(&json_trait1).unwrap();
427
428 assert!(trait1 == d_trait1);
429 }
430
431 #[test]
432 fn test_json_textual_from_str_ambal() {
433 let json_trait1 = r#"{
434 "name": "AmbalLoreFragment",
435 "records": [
436 {
437 "name": "banner",
438 "types": [
439 {"type": "Image"}
440 ]
441 },
442 {
443 "name": "content",
444 "types": [
445 {"type": {"String": "markdown"}}
446 ]
447 }
448 ]
449 }"#;
450
451 let d_trait1 = serde_json::from_str(&json_trait1).unwrap();
452
453 let trait1 = Trait {
454 name: "AmbalLoreFragment".to_string(),
455 records: vec![
456 (
457 "banner".to_string(),
458 vec![VariableTypeInfo {
459 type_: VariableType::Image,
460 default: None,
461 }
462 .into()],
463 )
464 .into(),
465 (
466 "content".to_string(),
467 vec![VariableTypeInfo {
468 type_: VariableType::String(Some(TextCategories::Markdown)),
469 default: None,
470 }
471 .into()],
472 )
473 .into(),
474 ],
475 };
476
477 assert!(trait1 == d_trait1);
478 }
479
480 #[test]
481 fn test_limits() {
482 let limits = Limits {
483 min: -100,
484 max: 100,
485 scale: 2,
486 };
487
488 let expected_min = -1.0;
489 let expected_max = 1.0;
490
491 let float_min = limits.min as f64 / 10u64.pow(limits.scale) as f64;
494 let float_max = limits.max as f64 / 10u64.pow(limits.scale) as f64;
495
496 assert_eq!(float_min, expected_min);
497 assert_eq!(float_max, expected_max);
498
499 let encoded = limits.encode();
500 let decoded = Limits::decode(&mut encoded.as_slice()).unwrap();
501 assert!(limits == decoded);
502 }
503}