Skip to main content

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        let normalized = s.to_ascii_lowercase();
295        match normalized.as_str() {
296            "boolean" => Ok(AttributeType::Boolean),
297            "binary" => Ok(AttributeType::Binary),
298            "text" => Ok(AttributeType::Text),
299            "numeric" => Ok(AttributeType::Numeric),
300            "datetime" => Ok(AttributeType::DateTime),
301            _ => Err(()),
302        }
303    }
304}
305
306#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq, Hash)]
307pub struct BundleContent {
308    pub said: ReferenceAttrType,
309}
310
311#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq)]
312pub struct CaptureContent {
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub attributes: Option<IndexMap<String, NestedAttrType>>,
315}
316
317#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq)]
318pub struct OverlayContent {
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub properties: Option<IndexMap<String, NestedValue>>,
321    pub overlay_def: OverlayDef,
322}
323
324#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq, Hash)]
325#[serde(untagged)]
326/// Enum representing type supported in bundle (From command)
327///
328/// References: supports ref said and ref name
329pub enum ReferenceAttrType {
330    Reference(RefValue),
331}
332
333impl fmt::Display for ReferenceAttrType {
334    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335        match self {
336            ReferenceAttrType::Reference(ref_value) => write!(f, "reference {}", ref_value),
337        }
338    }
339}
340
341#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Eq)]
342#[serde(untagged)]
343pub enum NestedValue {
344    Reference(RefValue),
345    Value(String),
346    Object(IndexMap<String, NestedValue>),
347    Array(Vec<NestedValue>),
348}
349impl NestedValue {
350    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
351        match self {
352            NestedValue::Reference(ref_value) => {
353                ref_value.hash(state);
354            }
355            NestedValue::Value(value) => {
356                value.hash(state);
357            }
358            NestedValue::Object(object) => {
359                for (key, value) in object {
360                    key.hash(state);
361                    value.hash(state);
362                }
363            }
364            NestedValue::Array(array) => {
365                for value in array {
366                    value.hash(state);
367                }
368            }
369        }
370    }
371    pub fn is_object(&self) -> bool {
372        matches!(self, NestedValue::Object(_))
373    }
374
375    pub fn is_array(&self) -> bool {
376        matches!(self, NestedValue::Array(_))
377    }
378
379    pub fn is_reference(&self) -> bool {
380        matches!(self, NestedValue::Reference(_))
381    }
382}
383
384#[derive(Debug, PartialEq, Clone, Hash, Eq)]
385pub enum RefValue {
386    Said(said::SelfAddressingIdentifier),
387    // This type is supported only for local-reference feature from facade (oca)
388    Name(String),
389}
390
391impl FromStr for RefValue {
392    type Err = RefValueParsingError;
393
394    fn from_str(s: &str) -> Result<Self, Self::Err> {
395        let (tag, rest) = s
396            .split_once(':')
397            .ok_or(RefValueParsingError::MissingColon)?;
398        match tag {
399            "refs" => {
400                let said = SelfAddressingIdentifier::from_str(rest)?;
401                Ok(RefValue::Said(said))
402            }
403            "refn" => Ok(RefValue::Name(rest.to_string())),
404            _ => Err(RefValueParsingError::UnknownTag(tag.to_string())),
405        }
406    }
407}
408
409#[derive(Error, Debug)]
410
411pub enum RefValueParsingError {
412    #[error("Missing colon")]
413    MissingColon,
414    #[error("Unknown tag `{0}`. Referece need to start with `refs` od `refn`")]
415    UnknownTag(String),
416    #[error("Invalid said: {0}")]
417    SaidError(#[from] said::error::Error),
418}
419
420impl fmt::Display for RefValue {
421    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422        match &self {
423            RefValue::Said(said) => write!(f, "refs:{}", said),
424            RefValue::Name(name) => write!(f, "refn:{}", name),
425        }
426    }
427}
428impl Serialize for RefValue {
429    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
430    where
431        S: Serializer,
432    {
433        match &self {
434            RefValue::Said(said) => serializer.serialize_str(format!("refs:{}", said).as_str()),
435            RefValue::Name(name) => serializer.serialize_str(format!("refn:{}", name).as_str()),
436        }
437    }
438}
439
440impl<'de> Deserialize<'de> for RefValue {
441    fn deserialize<D>(deserializer: D) -> Result<RefValue, D::Error>
442    where
443        D: Deserializer<'de>,
444    {
445        let s = String::deserialize(deserializer)?;
446        let (tag, rest) = s.split_once(':').ok_or(serde::de::Error::custom(format!(
447            "invalid reference: {}",
448            s
449        )))?;
450        match tag {
451            "refs" => {
452                let said = SelfAddressingIdentifier::from_str(rest);
453                match said {
454                    Ok(said) => Ok(RefValue::Said(said)),
455                    Err(_) => Err(serde::de::Error::custom(format!(
456                        "invalid reference: {}",
457                        s
458                    ))),
459                }
460            }
461            "refn" => Ok(RefValue::Name(rest.to_string())),
462            _ => Err(serde::de::Error::custom(format!(
463                "unknown reference type: {}",
464                tag
465            ))),
466        }
467    }
468}
469
470impl OCAAst {
471    pub fn new() -> Self {
472        OCAAst {
473            // Version of OCA specification
474            version: String::from("2.0.0"),
475            commands: Vec::new(),
476            commands_meta: IndexMap::new(),
477            meta: HashMap::new(),
478        }
479    }
480}
481
482impl Default for OCAAst {
483    fn default() -> Self {
484        Self::new()
485    }
486}
487
488#[cfg(test)]
489mod tests {
490    use indexmap::indexmap;
491    use overlay_file::overlay_registry::{OverlayLocalRegistry, OverlayRegistry};
492
493    use super::*;
494
495    #[test]
496    fn test_ocaast_serialize() {
497        let _ = env_logger::builder().is_test(true).try_init();
498        let mut attributes = IndexMap::new();
499        let mut properties = IndexMap::new();
500
501        let arr = NestedAttrType::Array(Box::new(NestedAttrType::Value(AttributeType::Boolean)));
502        attributes.insert("allowed".to_string(), arr);
503        attributes.insert(
504            "test".to_string(),
505            NestedAttrType::Value(AttributeType::Text),
506        );
507
508        properties.insert("test".to_string(), NestedValue::Value("test".to_string()));
509        let command = Command {
510            kind: CommandType::Add,
511            object_kind: ObjectKind::CaptureBase(CaptureContent {
512                attributes: Some(attributes),
513            }),
514        };
515
516        let overlay_registry =
517            OverlayLocalRegistry::from_dir("../overlay-file/core_overlays/").unwrap();
518        assert_eq!(overlay_registry.list_all().len(), 13);
519
520        let label_overlay_def = overlay_registry.get_overlay("Label/2.0.0").unwrap();
521        assert_eq!(label_overlay_def.get_full_name(), "label/2.0.0");
522
523        let mut label_props = IndexMap::new();
524        label_props.insert(
525            "language".to_string(),
526            NestedValue::Value("pl-PL".to_string()),
527        );
528        let attr_labels =
529            indexmap! { "allowed".to_string() => NestedValue::Value("Dopuszczony".to_string())};
530        let labels = NestedValue::Object(attr_labels.clone());
531        label_props.insert("attribute_labels".to_string(), labels);
532
533        let lable_command = Command {
534            kind: CommandType::Add,
535            object_kind: ObjectKind::Overlay(OverlayContent {
536                properties: Some(label_props),
537                overlay_def: label_overlay_def.clone(),
538            }),
539        };
540
541        let mut ocaast = OCAAst::new();
542        ocaast.commands.push(command);
543        ocaast.commands.push(lable_command);
544
545        // let wrapped = OCAAstWithRegistry {
546        //     ast: &ocaast,
547        //     registry: &overlay_registry,
548        // };
549
550        let serialized = serde_json::to_string(&ocaast).unwrap();
551
552        assert_eq!(
553            serialized,
554            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","unique_keys":["language"],"elements":[{"name":"language","keys":"Text","values":"Lang"},{"name":"attribute_labels","keys":"AttrNames","values":"Text"}]}}}],"commands_meta":{},"meta":{}}"#
555        );
556
557        let ast = serde_json::from_str::<OCAAst>(&serialized).unwrap();
558        assert_eq!(ocaast.version, ast.version);
559        assert_eq!(
560            ocaast
561                .commands
562                .last()
563                .unwrap()
564                .object_kind
565                .overlay_content()
566                .unwrap()
567                .overlay_def
568                .get_full_name(),
569            "label/2.0.0"
570        );
571        let content = ocaast
572            .commands
573            .last()
574            .unwrap()
575            .object_kind
576            .overlay_content()
577            .unwrap();
578        let props = content.properties.clone().unwrap();
579        let attr_labels = props.get("attribute_labels").unwrap();
580        let to_owned = if let NestedValue::Object(obj) = attr_labels {
581            obj.get("allowed")
582        } else {
583            None
584        };
585
586        assert_eq!(
587            to_owned.unwrap().clone(),
588            NestedValue::Value("Dopuszczony".to_string())
589        );
590
591        assert_eq!(ocaast, ast);
592    }
593}