oca_ast_semantics/ast/
attributes.rs

1use recursion::ExpandableExt;
2use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
3use std::hash::Hash;
4use wasm_bindgen::JsValue;
5
6use super::{
7    error::AttributeError,
8    recursive_attributes::{AttributeTypeResult, NestedAttrTypeFrame},
9    AttributeType, RefValue,
10};
11
12#[derive(Debug, PartialEq, Clone, Eq, Serialize)]
13#[serde(untagged)]
14/// Enum representing attribute type which can be nested.
15///
16/// References: supports ref said and ref name
17/// Value: supports all AttributeType
18/// Object: can be inline object which can have nested attributes types
19/// Array: is an array of specific type (only one type allowed)
20pub enum NestedAttrType {
21    Reference(RefValue),
22    Value(AttributeType),
23    #[serde(serialize_with = "array_serializer")]
24    Array(Box<NestedAttrType>),
25    /// Indicator that attribute was removed and does not need any type
26    Null,
27}
28
29impl NestedAttrType {
30    pub fn to_js_value(&self) -> Result<JsValue, JsValue> {
31        Ok(serde_wasm_bindgen::to_value(self)?)
32    }
33
34    pub fn from_js_value(value: JsValue) -> Result<Self, JsValue> {
35        Ok(serde_wasm_bindgen::from_value(value)?)
36    }
37}
38
39fn array_serializer<S>(arr: &NestedAttrType, serializer: S) -> Result<S::Ok, S::Error>
40where
41    S: Serializer,
42{
43    // Serialize the inner value as an array
44    let mut seq = serializer.serialize_seq(Some(1))?;
45    seq.serialize_element(&arr)?;
46    seq.end()
47
48    // Serialize the inner value and combine it with "Array"
49    // let serialized = serde_json::to_string(&foo).unwrap();
50    // let serialized_with_array = format!("Array[{}]", serialized);
51}
52
53impl Hash for NestedAttrType {
54    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
55        match self {
56            NestedAttrType::Reference(ref_value) => {
57                ref_value.hash(state);
58            }
59            NestedAttrType::Value(attr_type) => {
60                attr_type.hash(state);
61            }
62            NestedAttrType::Array(array) => {
63                array.hash(state);
64            }
65            NestedAttrType::Null => {
66                "null".hash(state);
67            }
68        }
69    }
70}
71
72impl<'de> Deserialize<'de> for NestedAttrType {
73    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
74    where
75        D: Deserializer<'de>,
76    {
77        let input: serde_json::Value = serde_json::Value::deserialize(deserializer)?;
78
79        let expanded = AttributeTypeResult::expand_frames(input, |seed| match seed {
80            serde_json::Value::Null => NestedAttrTypeFrame::Null.into(),
81            serde_json::Value::String(text) => match &text.get(..5) {
82                Some("refs:") | Some("refn:") => {
83                    let res = text.parse::<RefValue>();
84                    match res {
85                        Ok(ref_value) => NestedAttrTypeFrame::Reference(ref_value).into(),
86                        Err(e) => AttributeError::from(e).into(),
87                    }
88                }
89                _ => {
90                    let res = text.parse::<AttributeType>();
91                    match res {
92                        Ok(attr_type) => NestedAttrTypeFrame::Value(attr_type).into(),
93                        Err(_) => AttributeError::UnknownAttributeType(text).into(),
94                    }
95                }
96            },
97            serde_json::Value::Array(arr) => NestedAttrTypeFrame::Array(arr[0].clone()).into(),
98            value => {
99                AttributeError::ConvertingFailure(serde_json::to_string(&value).unwrap()).into()
100            }
101        });
102        match expanded.value() {
103            Ok(el) => Ok(el),
104            Err(er) => Err(serde::de::Error::custom(er)),
105        }
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use said::derivation::{HashFunction, HashFunctionCode};
112
113    use crate::ast::{error::AttributeError, AttributeType, NestedAttrType, RefValue};
114
115    #[test]
116    fn test_nested_array_attribute_type_serialization() {
117        let arr = NestedAttrType::Array(Box::new(NestedAttrType::Array(Box::new(
118            NestedAttrType::Value(AttributeType::Boolean),
119        ))));
120        let said = HashFunction::from(HashFunctionCode::Blake3_256).derive("fff".as_bytes());
121
122        let serialized = serde_json::to_string(&arr).unwrap();
123        let expected = r#"[["Boolean"]]"#;
124        assert_eq!(expected, serialized);
125
126        let deser: NestedAttrType = serde_json::from_str(&serialized).unwrap();
127        assert_eq!(arr, deser);
128
129        let attributes =
130            NestedAttrType::Array(Box::new(NestedAttrType::Reference(RefValue::Said(said))));
131
132        let serialized = serde_json::to_string(&attributes).unwrap();
133        let expected = r#"["refs:EEokfxxqwAM08iku7VHMaVFBaEGYVi2W-ctBKaTW6QdJ"]"#;
134        assert_eq!(expected, serialized);
135
136        let deser: NestedAttrType = serde_json::from_str(&serialized).unwrap();
137        assert_eq!(attributes, deser);
138    }
139
140    #[test]
141    fn test_nested_attribute_deserialize() {
142        let serialized = r#"["Numeric"]"#;
143        let deser: NestedAttrType = serde_json::from_str(serialized).unwrap();
144        assert_eq!(
145            NestedAttrType::Array(Box::new(NestedAttrType::Value(AttributeType::Numeric))),
146            deser
147        );
148
149        let wrong_type = r#"["Wrong"]"#;
150        let deser = serde_json::from_str::<NestedAttrType>(wrong_type);
151        assert!(deser.is_err());
152        assert_eq!(
153            deser.unwrap_err().to_string(),
154            AttributeError::UnknownAttributeType("Wrong".to_string()).to_string()
155        );
156
157        let serialized = r#"["refs:EEokfxxqwAM08iku7VHMaVFBaEGYVi2W-ctBKaTW6QdJ"]"#;
158        let expected = NestedAttrType::Array(Box::new(NestedAttrType::Reference(RefValue::Said(
159            said::derivation::HashFunction::from(said::derivation::HashFunctionCode::Blake3_256)
160                .derive("fff".as_bytes()),
161        ))));
162        let deser: NestedAttrType = serde_json::from_str(serialized).unwrap();
163        assert_eq!(expected, deser);
164
165        let serialized = r#"["refn:bob"]"#;
166        let expected = NestedAttrType::Array(Box::new(NestedAttrType::Reference(RefValue::Name(
167            "bob".to_string(),
168        ))));
169        let deser: NestedAttrType = serde_json::from_str(serialized).unwrap();
170        assert_eq!(expected, deser);
171    }
172
173    #[test]
174    fn test_reference_attribute_deserialize() {
175        let wrong_said = r#"["refs:wrong_said"]"#;
176        let deser = serde_json::from_str::<NestedAttrType>(wrong_said);
177        assert_eq!(deser.unwrap_err().to_string(), "Invalid said: Unknown code");
178
179        let missing_ref_tag = r#"["EEokfxxqwAM08iku7VHMaVFBaEGYVi2W-ctBKaTW6QdJ"]"#;
180        let deser = serde_json::from_str::<NestedAttrType>(missing_ref_tag);
181        assert_eq!(
182            deser.unwrap_err().to_string(),
183            "Attribute type EEokfxxqwAM08iku7VHMaVFBaEGYVi2W-ctBKaTW6QdJ doesn't exist"
184        );
185    }
186}