oca_ast/ast/
mod.rs

1use indexmap::IndexMap;
2use overlay_file::OverlayDef;
3use said::SelfAddressingIdentifier;
4use serde::ser::SerializeStruct;
5use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
6use std::hash::Hash;
7use std::{collections::HashMap, fmt, str::FromStr};
8use strum_macros::Display;
9use thiserror::Error;
10use wasm_bindgen::prelude::*;
11
12pub use self::attributes::NestedAttrType;
13
14pub mod attributes;
15pub mod error;
16pub mod recursive_attributes;
17
18#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
19pub struct OCAAst {
20    pub version: String,
21    pub commands: Vec<Command>,
22    pub commands_meta: IndexMap<usize, CommandMeta>,
23    pub meta: HashMap<String, String>,
24}
25
26#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
27pub struct Command {
28    #[serde(rename = "type")]
29    pub kind: CommandType,
30    #[serde(flatten)]
31    pub object_kind: ObjectKind,
32}
33
34#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
35pub struct CommandMeta {
36    pub line_number: usize,
37    pub raw_line: String,
38}
39
40#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
41pub enum CommandType {
42    Add,
43    Remove,
44    Modify,
45    From,
46}
47
48#[derive(Debug, PartialEq, Clone, Eq)]
49pub enum ObjectKind {
50    CaptureBase(CaptureContent),
51    OCABundle(BundleContent),
52    Overlay(OverlayContent),
53}
54
55impl fmt::Display for Command {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        write!(f, "{} ", self.kind)?;
58
59        match &self.object_kind {
60            ObjectKind::CaptureBase(content) => {
61                write!(f, "ATTRIBUTE")?;
62                if let Some(attributes) = &content.attributes {
63                    for (key, value) in attributes {
64                        write!(f, " {}={}", key, value)?;
65                    }
66                }
67            }
68            ObjectKind::OCABundle(content) => {
69                write!(f, "OCABUNDLE {}", content.said)?;
70            }
71            ObjectKind::Overlay(content) => {
72                write!(f, "OVERLAY {}", content.overlay_def.get_full_name())?;
73                if let Some(properties) = &content.properties {
74                    for (key, value) in properties {
75                        write!(f, " {}={}", key, value)?;
76                    }
77                }
78            }
79        }
80        Ok(())
81    }
82}
83
84// Implement Display for CommandType
85impl fmt::Display for CommandType {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        match self {
88            CommandType::Add => write!(f, "ADD"),
89            CommandType::Remove => write!(f, "REMOVE"),
90            CommandType::Modify => write!(f, "MODIFY"),
91            CommandType::From => write!(f, "FROM"),
92        }
93    }
94}
95
96// Implement Display for NestedAttrType
97impl fmt::Display for NestedAttrType {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        match self {
100            NestedAttrType::Value(attr_type) => write!(f, "{}", attr_type),
101            NestedAttrType::Array(nested) => write!(f, "[{}]", nested),
102            NestedAttrType::Null => write!(f, "Null"),
103            NestedAttrType::Reference(ref_value) => write!(f, "reference {}", ref_value),
104        }
105    }
106}
107
108// Implement Display for NestedValue
109impl fmt::Display for NestedValue {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match self {
112            NestedValue::Reference(ref_value) => write!(f, "{}", ref_value),
113            NestedValue::Value(value) => write!(f, "{}", value),
114            NestedValue::Object(object) => {
115                write!(f, "{{")?;
116                for (i, (key, value)) in object.iter().enumerate() {
117                    if i > 0 {
118                        write!(f, ", ")?;
119                    }
120                    write!(f, "\"{}\": {}", key, value)?;
121                }
122                write!(f, "}}")
123            }
124            NestedValue::Array(array) => {
125                write!(f, "[")?;
126                for (i, value) in array.iter().enumerate() {
127                    if i > 0 {
128                        write!(f, ", ")?;
129                    }
130                    write!(f, "{}", value)?;
131                }
132                write!(f, "]")
133            }
134        }
135    }
136}
137
138impl Serialize for ObjectKind {
139    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140    where
141        S: Serializer,
142    {
143        let mut state = serializer.serialize_struct("ObjectKind", 3)?;
144        match self {
145            ObjectKind::CaptureBase(content) => {
146                state.serialize_field("object_kind", "CaptureBase")?;
147                state.serialize_field("content", content)?;
148            }
149            ObjectKind::OCABundle(content) => {
150                state.serialize_field("object_kind", "OCABundle")?;
151                state.serialize_field("content", content)?;
152            }
153            ObjectKind::Overlay(content) => {
154                state.serialize_field("object_kind", "Overlay")?;
155                state.serialize_field("content", content)?;
156            }
157        }
158        state.end()
159    }
160}
161
162impl<'de> Deserialize<'de> for ObjectKind {
163    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
164    where
165        D: Deserializer<'de>,
166    {
167        #[derive(Deserialize)]
168        struct Raw {
169            #[serde(rename = "object_kind")]
170            kind: String,
171            content: serde_json::Value,
172        }
173
174        let raw = Raw::deserialize(deserializer)?;
175
176        match raw.kind.as_str() {
177            "CaptureBase" => Ok(ObjectKind::CaptureBase(
178                serde_json::from_value(raw.content).map_err(de::Error::custom)?,
179            )),
180            "OCABundle" => Ok(ObjectKind::OCABundle(
181                serde_json::from_value(raw.content).map_err(de::Error::custom)?,
182            )),
183            "Overlay" => Ok(ObjectKind::Overlay(
184                serde_json::from_value(raw.content).map_err(de::Error::custom)?,
185            )),
186            _ => Err(de::Error::custom(format!(
187                "Unknown object kind: {}",
188                raw.kind
189            ))),
190        }
191    }
192}
193
194impl From<u8> for ObjectKind {
195    fn from(val: u8) -> Self {
196        match val {
197            0 => ObjectKind::OCABundle(BundleContent {
198                said: ReferenceAttrType::Reference(RefValue::Name("".to_string())),
199            }),
200            1 => ObjectKind::CaptureBase(CaptureContent { attributes: None }),
201            2 => ObjectKind::Overlay(OverlayContent {
202                properties: None,
203                overlay_def: OverlayDef::default(),
204            }),
205            _ => panic!("Invalid ObjectKind value"),
206        }
207    }
208}
209
210impl From<ObjectKind> for u8 {
211    fn from(val: ObjectKind) -> Self {
212        match val {
213            ObjectKind::OCABundle(_) => 0,
214            ObjectKind::CaptureBase(_) => 1,
215            ObjectKind::Overlay(_) => 2,
216        }
217    }
218}
219
220pub struct OverlayInstance<'a> {
221    pub schema: &'a OverlayDef,
222    pub content: &'a OverlayContent,
223}
224
225impl Hash for ObjectKind {
226    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
227        match self {
228            ObjectKind::CaptureBase(content) => {
229                content.hash(state);
230            }
231            ObjectKind::OCABundle(content) => {
232                content.hash(state);
233            }
234            // TODO hash over content as well?
235            ObjectKind::Overlay(content) => {
236                content.overlay_def.hash(state);
237                if let Some(properties) = &content.properties {
238                    for (key, value) in properties {
239                        key.hash(state);
240                        value.hash(state);
241                    }
242                }
243            }
244        }
245    }
246}
247
248impl Hash for CaptureContent {
249    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
250        if let Some(attributes) = &self.attributes {
251            for (key, value) in attributes {
252                key.hash(state);
253                value.hash(state);
254            }
255        }
256    }
257}
258
259impl ObjectKind {
260    pub fn capture_content(&self) -> Option<&CaptureContent> {
261        match self {
262            ObjectKind::CaptureBase(content) => Some(content),
263            _ => None,
264        }
265    }
266
267    pub fn overlay_content(&self) -> Option<&OverlayContent> {
268        match self {
269            ObjectKind::Overlay(content) => Some(content),
270            _ => None,
271        }
272    }
273    pub fn oca_bundle_content(&self) -> Option<&BundleContent> {
274        match self {
275            ObjectKind::OCABundle(content) => Some(content),
276            _ => None,
277        }
278    }
279}
280#[wasm_bindgen]
281#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy, Display, Eq, Hash)]
282pub enum AttributeType {
283    Boolean,
284    Binary,
285    Text,
286    Numeric,
287    DateTime,
288}
289
290impl FromStr for AttributeType {
291    type Err = ();
292
293    fn from_str(s: &str) -> Result<Self, Self::Err> {
294        match s {
295            "Boolean" => Ok(AttributeType::Boolean),
296            "Binary" => Ok(AttributeType::Binary),
297            "Text" => Ok(AttributeType::Text),
298            "Numeric" => Ok(AttributeType::Numeric),
299            "DateTime" => Ok(AttributeType::DateTime),
300            _ => Err(()),
301        }
302    }
303}
304
305#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq, Hash)]
306pub struct BundleContent {
307    pub said: ReferenceAttrType,
308}
309
310#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq)]
311pub struct CaptureContent {
312    #[serde(skip_serializing_if = "Option::is_none")]
313    pub attributes: Option<IndexMap<String, NestedAttrType>>,
314}
315
316#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq)]
317pub struct OverlayContent {
318    #[serde(skip_serializing_if = "Option::is_none")]
319    pub properties: Option<IndexMap<String, NestedValue>>,
320    pub overlay_def: OverlayDef,
321}
322
323#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq, Hash)]
324#[serde(untagged)]
325/// Enum representing type supported in bundle (From command)
326///
327/// References: supports ref said and ref name
328pub enum ReferenceAttrType {
329    Reference(RefValue),
330}
331
332impl fmt::Display for ReferenceAttrType {
333    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334        match self {
335            ReferenceAttrType::Reference(ref_value) => write!(f, "reference {}", ref_value),
336        }
337    }
338}
339
340#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq)]
341#[serde(untagged)]
342pub enum NestedValue {
343    Reference(RefValue),
344    Value(String),
345    Object(IndexMap<String, NestedValue>),
346    Array(Vec<NestedValue>),
347}
348impl NestedValue {
349    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
350        match self {
351            NestedValue::Reference(ref_value) => {
352                ref_value.hash(state);
353            }
354            NestedValue::Value(value) => {
355                value.hash(state);
356            }
357            NestedValue::Object(object) => {
358                for (key, value) in object {
359                    key.hash(state);
360                    value.hash(state);
361                }
362            }
363            NestedValue::Array(array) => {
364                for value in array {
365                    value.hash(state);
366                }
367            }
368        }
369    }
370    pub fn is_object(&self) -> bool {
371        matches!(self, NestedValue::Object(_))
372    }
373
374    pub fn is_array(&self) -> bool {
375        matches!(self, NestedValue::Array(_))
376    }
377
378    pub fn is_reference(&self) -> bool {
379        matches!(self, NestedValue::Reference(_))
380    }
381}
382
383#[derive(Debug, PartialEq, Clone, Hash, Eq)]
384pub enum RefValue {
385    Said(said::SelfAddressingIdentifier),
386    // This type is supported only for local-reference feature from facade (oca)
387    Name(String),
388}
389
390impl FromStr for RefValue {
391    type Err = RefValueParsingError;
392
393    fn from_str(s: &str) -> Result<Self, Self::Err> {
394        let (tag, rest) = s
395            .split_once(':')
396            .ok_or(RefValueParsingError::MissingColon)?;
397        match tag {
398            "refs" => {
399                let said = SelfAddressingIdentifier::from_str(rest)?;
400                Ok(RefValue::Said(said))
401            }
402            "refn" => Ok(RefValue::Name(rest.to_string())),
403            _ => Err(RefValueParsingError::UnknownTag(tag.to_string())),
404        }
405    }
406}
407
408#[derive(Error, Debug)]
409
410pub enum RefValueParsingError {
411    #[error("Missing colon")]
412    MissingColon,
413    #[error("Unknown tag `{0}`. Referece need to start with `refs` od `refn`")]
414    UnknownTag(String),
415    #[error("Invalid said: {0}")]
416    SaidError(#[from] said::error::Error),
417}
418
419impl fmt::Display for RefValue {
420    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
421        match &self {
422            RefValue::Said(said) => write!(f, "refs:{}", said),
423            RefValue::Name(name) => write!(f, "refn:{}", name),
424        }
425    }
426}
427impl Serialize for RefValue {
428    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
429    where
430        S: Serializer,
431    {
432        match &self {
433            RefValue::Said(said) => serializer.serialize_str(format!("refs:{}", said).as_str()),
434            RefValue::Name(name) => serializer.serialize_str(format!("refn:{}", name).as_str()),
435        }
436    }
437}
438
439impl<'de> Deserialize<'de> for RefValue {
440    fn deserialize<D>(deserializer: D) -> Result<RefValue, D::Error>
441    where
442        D: Deserializer<'de>,
443    {
444        let s = String::deserialize(deserializer)?;
445        let (tag, rest) = s.split_once(':').ok_or(serde::de::Error::custom(format!(
446            "invalid reference: {}",
447            s
448        )))?;
449        match tag {
450            "refs" => {
451                let said = SelfAddressingIdentifier::from_str(rest);
452                match said {
453                    Ok(said) => Ok(RefValue::Said(said)),
454                    Err(_) => Err(serde::de::Error::custom(format!(
455                        "invalid reference: {}",
456                        s
457                    ))),
458                }
459            }
460            "refn" => Ok(RefValue::Name(rest.to_string())),
461            _ => Err(serde::de::Error::custom(format!(
462                "unknown reference type: {}",
463                tag
464            ))),
465        }
466    }
467}
468
469impl OCAAst {
470    pub fn new() -> Self {
471        OCAAst {
472            // Version of OCA specification
473            version: String::from("2.0.0"),
474            commands: Vec::new(),
475            commands_meta: IndexMap::new(),
476            meta: HashMap::new(),
477        }
478    }
479}
480
481impl Default for OCAAst {
482    fn default() -> Self {
483        Self::new()
484    }
485}
486
487#[cfg(test)]
488mod tests {
489    use indexmap::indexmap;
490    use overlay_file::overlay_registry::{OverlayLocalRegistry, OverlayRegistry};
491
492    use super::*;
493
494    #[test]
495    fn test_ocaast_serialize() {
496        let _ = env_logger::builder().is_test(true).try_init();
497        let mut attributes = IndexMap::new();
498        let mut properties = IndexMap::new();
499
500        let arr = NestedAttrType::Array(Box::new(NestedAttrType::Value(AttributeType::Boolean)));
501        attributes.insert("allowed".to_string(), arr);
502        attributes.insert(
503            "test".to_string(),
504            NestedAttrType::Value(AttributeType::Text),
505        );
506
507        properties.insert("test".to_string(), NestedValue::Value("test".to_string()));
508        let command = Command {
509            kind: CommandType::Add,
510            object_kind: ObjectKind::CaptureBase(CaptureContent {
511                attributes: Some(attributes),
512            }),
513        };
514
515        let overlay_registry =
516            OverlayLocalRegistry::from_dir("../overlay-file/core_overlays/").unwrap();
517        assert_eq!(overlay_registry.list_all().len(), 13);
518
519        let label_overlay_def = overlay_registry.get_by_fqn("Label/2.0.0").unwrap();
520        assert_eq!(label_overlay_def.get_full_name(), "label/2.0.0");
521
522        let mut label_props = IndexMap::new();
523        label_props.insert(
524            "language".to_string(),
525            NestedValue::Value("pl-PL".to_string()),
526        );
527        let attr_labels =
528            indexmap! { "allowed".to_string() => NestedValue::Value("Dopuszczony".to_string())};
529        let labels = NestedValue::Object(attr_labels.clone());
530        label_props.insert("attribute_labels".to_string(), labels);
531
532        let lable_command = Command {
533            kind: CommandType::Add,
534            object_kind: ObjectKind::Overlay(OverlayContent {
535                properties: Some(label_props),
536                overlay_def: label_overlay_def.clone(),
537            }),
538        };
539
540        let mut ocaast = OCAAst::new();
541        ocaast.commands.push(command);
542        ocaast.commands.push(lable_command);
543
544        // let wrapped = OCAAstWithRegistry {
545        //     ast: &ocaast,
546        //     registry: &overlay_registry,
547        // };
548
549        let serialized = serde_json::to_string(&ocaast).unwrap();
550
551        assert_eq!(
552            serialized,
553            r#"{"version":"2.0.0","commands":[{"type":"Add","object_kind":"CaptureBase","content":{"attributes":{"allowed":["Boolean"],"test":"Text"}}},{"type":"Add","object_kind":"Overlay","content":{"properties":{"language":"pl-PL","attribute_labels":{"allowed":"Dopuszczony"}},"overlay_def":{"namespace":null,"name":"label","version":"2.0.0","elements":[{"name":"language","keys":"Text","values":"Lang"},{"name":"attribute_labels","keys":"AttrNames","values":"Text"}]}}}],"commands_meta":{},"meta":{}}"#
554        );
555
556        let ast = serde_json::from_str::<OCAAst>(&serialized).unwrap();
557        assert_eq!(ocaast.version, ast.version);
558        assert_eq!(
559            ocaast
560                .commands
561                .last()
562                .unwrap()
563                .object_kind
564                .overlay_content()
565                .unwrap()
566                .overlay_def
567                .get_full_name(),
568            "label/2.0.0"
569        );
570        let content = ocaast
571            .commands
572            .last()
573            .unwrap()
574            .object_kind
575            .overlay_content()
576            .unwrap();
577        let props = content.properties.clone().unwrap();
578        let attr_labels = props.get("attribute_labels").unwrap();
579        let to_owned = if let NestedValue::Object(obj) = attr_labels {
580            obj.get("allowed")
581        } else {
582            None
583        };
584
585        assert_eq!(
586            to_owned.unwrap().clone(),
587            NestedValue::Value("Dopuszczony".to_string())
588        );
589
590        assert_eq!(ocaast, ast);
591    }
592}