1use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
14use serde_tuple::{Deserialize_tuple, Serialize_tuple};
15use std::collections::HashMap;
16
17use crate::{taxonomy::*, String255, Value};
18
19#[derive(Debug, Default, Clone, PartialEq, Deserialize)]
20#[serde(default, rename_all = "PascalCase")]
21pub struct AvialModel {
22 pub name: String255,
23 pub key: String255,
24
25 pub data: u64,
27
28 pub attributes: Vec<ModelAttribute>,
29 pub properties: Vec<ModelProperty>,
30 pub facts: Vec<ModelFact>,
31}
32
33impl Serialize for AvialModel {
34 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35 where
36 S: Serializer,
37 {
38 let mut x = serializer.serialize_struct("AvialModel", 4)?;
39 x.serialize_field("Model", "Avial")?;
40 if !self.name.is_empty() {
41 x.serialize_field("Name", &self.name)?;
42 }
43 if !self.key.is_empty() {
44 x.serialize_field("Key", &self.key)?;
45 }
46 if self.data > 0 {
47 x.serialize_field("Data", &self.data)?;
48 }
49 if !self.attributes.is_empty() {
50 x.serialize_field("Attributes", &self.attributes)?;
51 }
52 if !self.properties.is_empty() {
53 x.serialize_field("Properties", &self.properties)?;
54 }
55 if !self.facts.is_empty() {
56 x.serialize_field("Facts", &self.facts)?;
57 }
58 x.end()
59 }
60}
61
62#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
63pub struct ModelAttribute {
64 pub attribute: Attribute,
65 pub value: Value,
66 pub traits: Vec<ModelTrait>,
67}
68
69#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
70pub struct ModelTrait {
71 pub name: String255,
72 pub key: String255,
73 pub value: Value,
74}
75
76#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
77pub struct ModelFact {
78 pub attribute: Attribute,
79 pub value: Value,
80 pub facets: Vec<Facet>,
81 pub features: Vec<ModelFeature>,
82 pub fields: Vec<ModelField>,
83 pub frames: Vec<ModelFrame>,
84}
85
86#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
87pub struct Facet {
88 pub name: String255,
89 pub value: Value,
90 pub factors: Vec<ModelFactor>,
91}
92
93#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
94pub struct ModelFactor {
95 pub key: String255,
96 pub value: Value,
97}
98
99#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
100pub struct ModelFeature {
101 pub name: String255,
102 pub key: String255,
103 pub value: Value,
104}
105
106#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
107pub struct ModelField {
108 pub name: String255,
109 pub default_value: Value,
110}
111
112#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
113pub struct ModelFrame {
114 pub key: String255,
115 pub values: Vec<Value>,
116}
117
118#[derive(Debug, Default, Clone, PartialEq, Serialize_tuple, Deserialize_tuple)]
119pub struct ModelProperty {
120 pub name: String255,
121 pub key: String255,
122 pub value: Value,
123 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
124 pub annotations: HashMap<Attribute, Value>,
125}
126
127#[cfg(test)]
128#[allow(clippy::approx_constant)]
129mod test {
130 use std::str::FromStr;
131
132 use ascii::{AsciiChar, AsciiString};
133 use maplit::hashmap;
134 use serde_json::json;
135 use time::macros::datetime;
136
137 use crate::{AvialError, Entity, Locutor, String255, Token, UnimplementedValue};
138
139 use super::*;
140
141 fn _test_ok(jsonobj: serde_json::Value, expected: AvialModel) {
142 let actual: AvialModel = match serde_json::from_value(jsonobj.clone()) {
143 Ok(v) => v,
144 Err(e) => {
145 panic!("Error deserializing: {}", e);
146 }
147 };
148 assert_eq!(actual, expected);
149
150 let ser = match serde_json::to_value(&expected) {
151 Ok(v) => v,
152 Err(e) => {
153 panic!("Error serializing: {}", e);
154 }
155 };
156 let actual_2: AvialModel = match serde_json::from_value(ser) {
157 Ok(v) => v,
158 Err(e) => {
159 panic!("Error deserializing again: {}", e);
160 }
161 };
162 assert_eq!(actual_2, expected);
163 }
164
165 fn _test_err(jsonobj: serde_json::Value) {
166 match serde_json::from_value::<AvialModel>(jsonobj.clone()) {
167 Ok(v) => {
168 panic!("Expected error from {jsonobj}, got: {:?}", v);
169 }
170 Err(e) => println!("OK: got expected error: {}", e),
171 }
172 }
173
174 #[test]
175 fn empty() {
176 _test_ok(
177 json!({}),
178 AvialModel {
179 name: String255::unchecked(""),
180 key: String255::unchecked(""),
181 data: 0,
182 attributes: vec![],
183 properties: vec![],
184 facts: vec![],
185 },
186 );
187 }
188
189 #[test]
190 fn only_name() {
191 _test_ok(
192 json!(
193 {
194 "Name": "test name"
195 }
196 ),
197 AvialModel {
198 name: String255::unchecked("test name"),
199 key: String255::unchecked(""),
200 data: 0,
201 attributes: vec![],
202 properties: vec![],
203 facts: vec![],
204 },
205 );
206 }
207
208 #[test]
209 fn only_key() {
210 _test_ok(
211 json!(
212 {
213 "Key": "test key"
214 }
215 ),
216 AvialModel {
217 name: String255::unchecked(""),
218 key: String255::unchecked("test key"),
219 data: 0,
220 attributes: vec![],
221 properties: vec![],
222 facts: vec![],
223 },
224 );
225 }
226
227 #[test]
228 fn only_data() {
229 _test_ok(
230 json!(
231 {
232 "Data": 123
233 }
234 ),
235 AvialModel {
236 name: String255::unchecked(""),
237 key: String255::unchecked(""),
238 data: 123,
239 attributes: vec![],
240 properties: vec![],
241 facts: vec![],
242 },
243 );
244 }
245
246 #[test]
247 fn only_properties() {
248 _test_ok(
249 json!(
250 {
251 "Properties": [
252 ["name1", "key1", { "INTEGER": "123" }],
253 ]
254 }),
255 AvialModel {
256 name: String255::unchecked(""),
257 key: String255::unchecked(""),
258 data: 0,
259 attributes: vec![],
260 properties: vec![ModelProperty {
261 name: String255::unchecked("name1"),
262 key: String255::unchecked("key1"),
263 value: Value::Integer(123),
264 annotations: HashMap::new(),
265 }],
266 facts: vec![],
267 },
268 )
269 }
270
271 #[test]
272 fn invalid_property() {
273 _test_err(json!({"Properties": 123}));
274 _test_err(json!({"Properties": [ ["name1", "key1", { "INTEGER": "123" }, "toto" ] ]}));
275 _test_err(json!({"Properties": [ ["name1", "key1", "abc", {} ] ]}));
276 _test_err(json!({"Properties": [ ["name1", "key1", [], {} ] ]}));
277 _test_err(json!({"Properties": [ ["name1", "key1", {}, {} ] ]}));
278 _test_err(json!({"Properties": [ ["name1", "key1", ] ]}));
279 _test_err(
280 json!({"Properties": [ ["name1", "key1", { "INTEGER": "123" }, {}, "extra field yayy" ] ]}),
281 );
282 }
283
284 #[test]
285 fn example_entity() {
286 _test_ok(
287 serde_json::from_str(include_str!("../test_json/example_entity_modified.json"))
288 .expect("Failed to parse example json file"),
289 AvialModel {
290 name: String255::unchecked("Example Entity"),
291 key: String255::unchecked("Example"),
292 data: 28,
293 attributes: vec![
294 ModelAttribute {
295 attribute: Attribute::Example,
296 value: Value::String(AsciiString::from_ascii("Attribute Value").unwrap()),
297 traits: vec![
298 ModelTrait {
299 name: String255::unchecked("Trait 1 Name"),
300 key: String255::unchecked("Trait 1 Key"),
301 value: Value::String(AsciiString::from_ascii("Trait 1 Value").unwrap()),
302 },
303 ModelTrait {
304 name: String255::unchecked("Trait 2 Name"),
305 key: String255::unchecked("Trait 2 Key"),
306 value: Value::String(AsciiString::from_ascii("Trait 2 Value").unwrap()),
307 },
308 ],
309 },
310 ],
311 properties: vec![
312 ModelProperty {
313 name: String255::unchecked("Null Property"),
314 key: String255::unchecked("Null"),
315 value: Value::Null("".to_string()),
316 annotations: HashMap::new(),
317 },
318 ModelProperty {
319 name: String255::unchecked("AvesTerra Property"),
320 key: String255::unchecked("AvesTerra"),
321 value: Value::Avesterra("Example".to_string()),
322 annotations: HashMap::new(),
323 },
324 ModelProperty {
325 name: String255::unchecked("Entity Property"),
326 key: String255::unchecked("Entity"),
327 value: Value::Entity(Entity::new(0, 0, 24)),
328 annotations: HashMap::new(),
329 },
330 ModelProperty {
331 name: String255::unchecked("Boolean Property"),
332 key: String255::unchecked("Boolean"),
333 value: Value::Boolean(true),
334 annotations: HashMap::new(),
335 },
336 ModelProperty {
337 name: String255::unchecked("Character Property"),
338 key: String255::unchecked("Character"),
339 value: Value::Character(AsciiChar::A),
340 annotations: HashMap::new(),
341 },
342 ModelProperty {
343 name: String255::unchecked("String Property"),
344 key: String255::unchecked("String"),
345 value: Value::String(
346 AsciiString::from_ascii("Hello World!\u{0007}").unwrap(),
347 ),
348 annotations: HashMap::new(),
349 },
350 ModelProperty {
351 name: String255::unchecked("Text Property"),
352 key: String255::unchecked("Text"),
353 value: Value::Text("ÅvësTêrrã".to_string()),
354 annotations: HashMap::new(),
355 },
356 ModelProperty {
357 name: String255::unchecked("Web Property"),
358 key: String255::unchecked("Web"),
359 value: Value::Web("www.example.com".to_string()),
360 annotations: HashMap::new(),
361 },
362 ModelProperty {
363 name: String255::unchecked("Interchange Property"),
364 key: String255::unchecked("Interchange"),
365 value: Value::Interchange(
366 r#"{"Greeting":"Hello World!\u0007"}"#.to_string(),
367 ),
368 annotations: HashMap::new(),
369 },
370 ModelProperty {
371 name: String255::unchecked("Integer Property"),
372 key: String255::unchecked("Integer"),
373 value: Value::Integer(42),
374 annotations: HashMap::new(),
375 },
376 ModelProperty {
377 name: String255::unchecked("Float Property"),
378 key: String255::unchecked("Float"),
379 value: Value::Float(3.14159),
380 annotations: HashMap::new(),
381 },
382 ModelProperty {
383 name: String255::unchecked("Time Property"),
384 key: String255::unchecked("Time"),
385 value: Value::Time(datetime!(2010-01-11 05:00:22 UTC)),
386 annotations: HashMap::new(),
387 },
388 ModelProperty {
389 name: String255::unchecked("Data Property"),
390 key: String255::unchecked("Data"),
391 value: Value::Data(vec![
392 0x45, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61,
393 0x20, 0x7E, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29,
394 0x5F, 0x2B, 0x22, 0x27,
395 ]),
396 annotations: HashMap::new(),
397 },
398 ModelProperty {
399 name: String255::unchecked("Exception Property"),
400 key: String255::unchecked("Exception"),
401 value: Value::Exception(AvialError {
402 error: Error::Halt,
403 message: "The machine has halted".to_string(),
404 }),
405 annotations: HashMap::new(),
406 },
407 ModelProperty {
408 name: String255::unchecked("Operator Property"),
409 key: String255::unchecked("Operator"),
410 value: Value::Operator(Operator::Halt),
411 annotations: HashMap::new(),
412 },
413 ModelProperty {
414 name: String255::unchecked("Function Property"),
415 key: String255::unchecked("Function"),
416 value: Value::Function(Entity::new(0, 0, 18)),
417 annotations: HashMap::new(),
418 },
419 ModelProperty {
420 name: String255::unchecked("Date Property"),
421 key: String255::unchecked("Date"),
422 value: Value::Date(UnimplementedValue(
423 "{\"YEAR\": 1960,\"MONTH\": 10,\"DAY\": 1}".to_string(),
424 )),
425 annotations: HashMap::new(),
426 },
427 ModelProperty {
428 name: String255::unchecked("Measurement Property"),
429 key: String255::unchecked("Measurement"),
430 value: Value::Measurement(UnimplementedValue(
431 "{\"FLOAT\": 4.20000000000000E+01,\"UNIT\": \"GRAM_UNIT\",\"PREFIX\": \"MICRO_PREFIX\",\"CONFIDENCE\": 9.99000015258789E+01,\"UNCERTAINTY\": 1.00000001490116E-01}".to_string(),
432 )),
433 annotations: HashMap::new(),
434 },
435 ModelProperty {
436 name: String255::unchecked("Authorization Property"),
437 key: String255::unchecked("Authorization"),
438 value: Value::Authorization(
439 Token::from_str("285bbdb0-4966-4047-9261-3e0ddd3f2fab").unwrap(),
440 ),
441 annotations: HashMap::new(),
442 },
443 ModelProperty {
444 name: String255::unchecked("Variable Property"),
445 key: String255::unchecked("Variable"),
446 value: Value::Variable(
447 "City Variable".to_string(), Box::new(Value::String(AsciiString::from_ascii(r"Washington, D.C.").unwrap()))
448 ),
449 annotations: HashMap::new(),
450 },
451 ModelProperty {
452 name: String255::unchecked("Array Property"),
453 key: String255::unchecked("Array"),
454 value: Value::Array(vec![
455 Value::Entity(Entity::new(0, 0, 24)),
456 Value::Integer(42),
457 Value::Array(vec![
458 Value::Character(AsciiChar::A),
459 Value::Boolean(true),
460 ]),
461 ]
462 ),
463 annotations: HashMap::new(),
464 },
465 ModelProperty {
466 name: String255::unchecked("Aggregate Property"),
467 key: String255::unchecked("Aggregate"),
468 value: Value::Aggregate(hashmap! {
469 "Boolean Variable".into() => Value::Boolean(true),
470 "Integer Variable".into() => Value::Integer(42),
471 "Array Variable".into() => Value::Array(vec![
472 Value::Entity(Entity::new(0, 0, 24)),
473 Value::Integer(42),
474 Value::Array(vec![
475 Value::Character(AsciiChar::A),
476 Value::Boolean(true),
477 ]),
478 ]),
479 }),
480 annotations: HashMap::new(),
481 },
482 ModelProperty {
483 name: String255::unchecked("Annotation Property"),
484 key: String255::unchecked("Annotation"),
485 value: Value::Null("".to_string()),
486 annotations: {
487 let mut map = HashMap::new();
488 map.insert(
489 Attribute::Example,
490 Value::String(AsciiString::from_ascii("Annotation Value").unwrap()),
491 );
492 map
493 },
494 },
495 ModelProperty {
496 name: String255::unchecked("Locutor Property"),
497 key: String255::unchecked("Locutor"),
498 value: Value::Locutor(Box::new(Locutor{
499 entity: Entity::new(0, 0, 24),
500 outlet: Entity::new(0, 0, 11),
501 auxiliary: Entity::new(0, 0, 1),
502 ancillary: Entity::new(0, 0, 2),
503 context: Context::Avesterra,
504 category: Category::Avesterra,
505 class: Class::Avesterra,
506 method: Method::Avesterra,
507 attribute: Attribute::Avesterra,
508 instance: 1,
509 name: String255::unchecked("Example Name"),
510 value: Value::String(AsciiString::from_ascii("Example String").unwrap()),
511 index: 1,
512 count: 123,
513 precedence: 8,
514 parameter: -1,
515 mode: Mode::Avesterra,
516 event: Event::Avesterra,
517 timeout: 60,
518 aspect: Aspect::Avesterra,
519 authority: Token::NOAUTH,
520 ..Default::default()
521 })),
522 annotations: HashMap::new(),
523 },
524 ],
525 facts: vec![ModelFact {
526 attribute: Attribute::Example,
527 value: Value::String(AsciiString::from_ascii("Fact Value").unwrap()),
528 facets: vec![
529 Facet {
530 name: String255::unchecked("Facet 1 Name"),
531 value: Value::String(AsciiString::from_ascii("Facet 1 Value").unwrap()),
532 factors: vec![
533 ModelFactor {
534 key: String255::unchecked("Facet 1 Fact 1 Key"),
535 value: Value::String(
536 AsciiString::from_ascii("Facet 1 Fact 1 Value").unwrap(),
537 ),
538 },
539 ModelFactor {
540 key: String255::unchecked("Facet 1 Fact 2 Key"),
541 value: Value::String(
542 AsciiString::from_ascii("Facet 1 Fact 2 Value").unwrap(),
543 ),
544 },
545 ],
546 },
547 Facet {
548 name: String255::unchecked("Facet 2 Name"),
549 value: Value::String(AsciiString::from_ascii("Facet 2 Value").unwrap()),
550 factors: vec![
551 ModelFactor {
552 key: String255::unchecked("Facet 2 Fact 1 Key"),
553 value: Value::String(
554 AsciiString::from_ascii("Facet 2 Fact 1 Value").unwrap(),
555 ),
556 },
557 ModelFactor {
558 key: String255::unchecked("Facet 2 Fact 2 Key"),
559 value: Value::String(
560 AsciiString::from_ascii("Facet 2 Fact 2 Value").unwrap(),
561 ),
562 },
563 ],
564 },
565 ],
566 features: vec![
567 ModelFeature {
568 name: String255::unchecked("Feature 1 Name"),
569 key: String255::unchecked("Feature 1 Key"),
570 value: Value::String(
571 AsciiString::from_ascii("Feature 1 Value").unwrap(),
572 ),
573 },
574 ModelFeature {
575 name: String255::unchecked("Feature 2 Name"),
576 key: String255::unchecked("Feature 2 Key"),
577 value: Value::String(
578 AsciiString::from_ascii("Feature 2 Value").unwrap(),
579 ),
580 },
581 ModelFeature {
582 name: String255::unchecked("Feature 3 Name"),
583 key: String255::unchecked("Feature 3 Key"),
584 value: Value::String(
585 AsciiString::from_ascii("Feature 3 Value").unwrap(),
586 ),
587 },
588 ],
589 fields: vec![
590 ModelField {
591 name: String255::unchecked("Field 1 Name"),
592 default_value: Value::String(
593 AsciiString::from_ascii("Field 1 Default Value").unwrap(),
594 ),
595 },
596 ModelField {
597 name: String255::unchecked("Field 2 Name"),
598 default_value: Value::String(
599 AsciiString::from_ascii("Field 2 Default Value").unwrap(),
600 ),
601 },
602 ModelField {
603 name: String255::unchecked("Field 3 Name"),
604 default_value: Value::String(
605 AsciiString::from_ascii("Field 3 Default Value").unwrap(),
606 ),
607 },
608 ],
609 frames: vec![
610 ModelFrame {
611 key: String255::unchecked("Frame 1 Key"),
612 values: vec![
613 Value::String(
614 AsciiString::from_ascii("Frame 1 Field 1 Value").unwrap(),
615 ),
616 Value::String(
617 AsciiString::from_ascii("Frame 1 Field 2 Value").unwrap(),
618 ),
619 Value::String(
620 AsciiString::from_ascii("Frame 1 Field 3 Value").unwrap(),
621 ),
622 ],
623 },
624 ModelFrame {
625 key: String255::unchecked("Frame 2 Key"),
626 values: vec![
627 Value::String(
628 AsciiString::from_ascii("Frame 2 Field 1 Value").unwrap(),
629 ),
630 Value::String(
631 AsciiString::from_ascii("Frame 2 Field 2 Value").unwrap(),
632 ),
633 Value::String(
634 AsciiString::from_ascii("Frame 2 Field 3 Value").unwrap(),
635 ),
636 ],
637 },
638 ModelFrame {
639 key: String255::unchecked("Frame 3 Key"),
640 values: vec![
641 Value::String(
642 AsciiString::from_ascii("Frame 3 Field 1 Value").unwrap(),
643 ),
644 Value::String(
645 AsciiString::from_ascii("Frame 3 Field 2 Value").unwrap(),
646 ),
647 Value::String(
648 AsciiString::from_ascii("Frame 3 Field 3 Value").unwrap(),
649 ),
650 ],
651 },
652 ],
653 }],
654 },
655 )
656 }
657}