use crate::categories::{BinaryCategories, TextCategories};
use parity_scale_codec::{Compact, Decode, Encode, Input, Output};
use scale_info::prelude::{boxed::Box, vec::Vec};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
type String = Vec<u8>;
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub struct Limits {
pub min: i64,
pub max: i64,
pub scale: u32,
}
fn wrap_to_u64(x: i64) -> u64 {
(x as u64).wrapping_add(u64::MAX / 2 + 1)
}
fn to_i64(x: u64) -> i64 {
((x as i64) ^ (1 << 63)) & (1 << 63) | (x & (u64::MAX >> 1)) as i64
}
impl Encode for Limits {
fn encode_to<W: Output + ?Sized>(&self, dest: &mut W) {
Compact(wrap_to_u64(self.min)).encode_to(dest);
Compact(wrap_to_u64(self.max)).encode_to(dest);
Compact(self.scale).encode_to(dest);
}
}
impl Decode for Limits {
fn decode<I: Input>(input: &mut I) -> Result<Self, parity_scale_codec::Error> {
Ok(Self {
min: to_i64(Compact::<u64>::decode(input)?.into()),
max: to_i64(Compact::<u64>::decode(input)?.into()),
scale: Compact::<u32>::decode(input)?.into(),
})
}
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Copy, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub enum CodeType {
Shards,
Wire {
looped: Option<bool>,
pure: Option<bool>,
},
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub struct CodeInfo {
pub kind: CodeType,
pub requires: Vec<(String, VariableType)>,
pub exposes: Vec<(String, VariableType)>,
pub inputs: Vec<VariableType>,
pub output: VariableType,
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub struct TableInfo {
pub keys: Vec<String>,
pub types: Vec<Vec<VariableType>>,
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub enum VariableType {
None,
Any,
Bool,
Color,
Bytes(Option<BinaryCategories>),
String(Option<TextCategories>),
Image,
Audio,
Mesh,
Enum {
#[codec(compact)]
vendor_id: u32,
#[codec(compact)]
type_id: u32,
},
Int(Option<Limits>),
Int2([Option<Limits>; 2]),
Int3([Option<Limits>; 3]),
Int4([Option<Limits>; 4]),
Int8([Option<Limits>; 8]),
Int16([Option<Limits>; 16]),
Float(Option<Limits>),
Float2([Option<Limits>; 2]),
Float3([Option<Limits>; 3]),
Float4([Option<Limits>; 4]),
Seq {
types: Vec<VariableType>,
length_limits: Option<Limits>,
},
Table(TableInfo),
Object {
#[codec(compact)]
vendor_id: u32,
#[codec(compact)]
type_id: u32,
},
Code(Box<CodeInfo>),
Channel(Box<VariableType>),
Event(Box<VariableType>),
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub struct VariableTypeInfo {
#[cfg_attr(feature = "std", serde(alias = "type"))]
pub type_: VariableType,
pub default: Option<Vec<u8>>,
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub struct Record {
pub name: String,
pub types: Vec<VariableTypeInfo>,
}
impl From<(String, Vec<VariableTypeInfo>)> for Record {
fn from((name, types): (String, Vec<VariableTypeInfo>)) -> Self {
Self { name, types }
}
}
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Clone, PartialEq, Debug, Eq, scale_info::TypeInfo)]
pub struct Trait {
pub name: String,
pub records: Vec<Record>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::categories::TextCategories;
#[test]
fn encode_decode_simple_1() {
let mut trait1: Vec<Record> = vec![(
"int1".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Int(None),
default: None,
}],
)
.into()];
trait1 = trait1
.into_iter()
.map(|r| (r.name.to_lowercase(), r.types).into())
.collect();
trait1.dedup_by(|a, b| a.name == b.name);
trait1.sort_by(|a, b| a.name.cmp(&b.name));
let trait1 = Trait {
name: "Trait1".to_string(),
records: trait1,
};
let e_trait1 = trait1.encode();
let d_trait1 = Trait::decode(&mut e_trait1.as_slice()).unwrap();
assert!(trait1 == d_trait1);
}
#[test]
fn encode_decode_boxed_1() {
let mut trait1: Vec<Record> = vec![
(
"int1".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Int(None),
default: None,
}],
)
.into(),
(
"boxed1".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Code(Box::new(CodeInfo {
kind: CodeType::Wire {
looped: None,
pure: None,
},
requires: vec![("int1".to_string(), VariableType::Int(None))],
exposes: vec![],
inputs: vec![],
output: VariableType::None,
})),
default: None,
}],
)
.into(),
];
trait1 = trait1
.into_iter()
.map(|r| (r.name.to_lowercase(), r.types).into())
.collect();
trait1.dedup_by(|a, b| a.name == b.name);
trait1.sort_by(|a, b| a.name.cmp(&b.name));
let trait1 = Trait {
name: "Trait1".to_string(),
records: trait1,
};
let e_trait1 = trait1.encode();
let d_trait1 = Trait::decode(&mut e_trait1.as_slice()).unwrap();
assert!(trait1 == d_trait1);
assert!(d_trait1.records[0].name == "boxed1".to_string());
let type_ = &d_trait1.records[0].types[0].type_;
let requires = match type_ {
VariableType::Code(code) => &code.requires,
_ => panic!("Should be a code"),
};
assert!(requires[0].0 == "int1".to_string());
}
#[test]
fn test_json_simple_1() {
let mut trait1: Vec<Record> = vec![(
"int1".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Int(None),
default: None,
}],
)
.into()];
trait1 = trait1
.into_iter()
.map(|r| (r.name.to_lowercase(), r.types).into())
.collect();
trait1.dedup_by(|a, b| a.name == b.name);
trait1.sort_by(|a, b| a.name.cmp(&b.name));
let trait1 = Trait {
name: "Trait1".to_string(),
records: trait1,
};
let e_trait1 = serde_json::to_string(&trait1).unwrap();
let d_trait1: Trait = serde_json::from_str(&e_trait1).unwrap();
assert!(trait1 == d_trait1);
}
#[test]
fn test_json_boxed_1() {
let mut trait1: Vec<Record> = vec![
(
"int1".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Int(None),
default: None,
}],
)
.into(),
(
"boxed1".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Code(Box::new(CodeInfo {
kind: CodeType::Wire {
looped: None,
pure: None,
},
requires: vec![("int1".to_string(), VariableType::Int(None))],
exposes: vec![],
inputs: vec![],
output: VariableType::None,
})),
default: None,
}],
)
.into(),
];
trait1 = trait1
.into_iter()
.map(|r| (r.name.to_lowercase(), r.types).into())
.collect();
trait1.dedup_by(|a, b| a.name == b.name);
trait1.sort_by(|a, b| a.name.cmp(&b.name));
let trait1 = Trait {
name: "Trait1".to_string(),
records: trait1,
};
let e_trait1 = serde_json::to_string(&trait1).unwrap();
let d_trait1: Trait = serde_json::from_str(&e_trait1).unwrap();
assert!(trait1 == d_trait1);
assert!(d_trait1.records[0].name == "boxed1".to_string());
let type_ = &d_trait1.records[0].types[0].type_;
let requires = match type_ {
VariableType::Code(code) => &code.requires,
_ => panic!("Should be a code"),
};
assert!(requires[0].0 == "int1".to_string());
}
#[test]
fn test_json_textual_from_str() {
let trait1 = Trait {
name: "Trait1".to_string(),
records: vec![(
"int1".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Int(None),
default: None,
}],
)
.into()],
};
let json_trait1 = r#"{
"name": "Trait1",
"records": [
{
"name": "int1",
"types": [
{
"type": {"Int": null},
"default": null
}
]
}
]
}"#;
let d_trait1 = serde_json::from_str(&json_trait1).unwrap();
assert!(trait1 == d_trait1);
}
#[test]
fn test_json_textual_from_str_ambal() {
let json_trait1 = r#"{
"name": "AmbalLoreFragment",
"records": [
{
"name": "banner",
"types": [
{"type": "Image"}
]
},
{
"name": "content",
"types": [
{"type": {"String": "markdown"}}
]
}
]
}"#;
let d_trait1 = serde_json::from_str(&json_trait1).unwrap();
let trait1 = Trait {
name: "AmbalLoreFragment".to_string(),
records: vec![
(
"banner".to_string(),
vec![VariableTypeInfo {
type_: VariableType::Image,
default: None,
}
.into()],
)
.into(),
(
"content".to_string(),
vec![VariableTypeInfo {
type_: VariableType::String(Some(TextCategories::Markdown)),
default: None,
}
.into()],
)
.into(),
],
};
assert!(trait1 == d_trait1);
}
#[test]
fn test_limits() {
let limits = Limits {
min: -100,
max: 100,
scale: 2,
};
let expected_min = -1.0;
let expected_max = 1.0;
let float_min = limits.min as f64 / 10u64.pow(limits.scale) as f64;
let float_max = limits.max as f64 / 10u64.pow(limits.scale) as f64;
assert_eq!(float_min, expected_min);
assert_eq!(float_max, expected_max);
let encoded = limits.encode();
let decoded = Limits::decode(&mut encoded.as_slice()).unwrap();
assert!(limits == decoded);
}
}