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
84impl 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
96impl 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
108impl 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 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)]
325pub 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 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: 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 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}