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