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 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)]
326pub 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 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: 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 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}