1use alloc::{borrow::Cow, boxed::Box, string::*, vec::Vec};
11use core::{
12 cmp::{self, Ordering},
13 fmt,
14 num::NonZeroU64,
15};
16
17use hashbrown::HashMap;
18use oxilangtag::LanguageTag;
19use serde::{Deserialize, Deserializer, Serialize, Serializer};
20use serde_json::Value;
21use serde_with::{serde_as, skip_serializing_none, DeserializeAs, OneOrMany, Same};
22use time::OffsetDateTime;
23
24use crate::{
25 builder::{data_schema::UncheckedDataSchema, ThingBuilder, ToExtend},
26 extend::ExtendableThing,
27 hlist::Nil,
28};
29
30pub(crate) type MultiLanguage = HashMap<LanguageTag<String>, String>;
31pub(crate) type DataSchemaMap<Other> = HashMap<
32 String,
33 DataSchema<
34 <Other as ExtendableThing>::DataSchema,
35 <Other as ExtendableThing>::ArraySchema,
36 <Other as ExtendableThing>::ObjectSchema,
37 >,
38>;
39
40pub const TD_CONTEXT_10: &str = "https://www.w3.org/2019/wot/td/v1";
43
44pub const TD_CONTEXT_11: &str = "https://www.w3.org/2022/wot/td/v1.1";
47
48mod rfc3339_option {
49 use core::fmt;
50
51 use alloc::format;
52 use serde::{ser::Error, Serialize, Serializer};
53 use time::OffsetDateTime;
54
55 pub use time::serde::rfc3339::option::deserialize;
56
57 pub fn serialize<S: Serializer>(
61 option: &Option<OffsetDateTime>,
62 serializer: S,
63 ) -> Result<S::Ok, S::Error> {
64 option
65 .map(|odt| {
66 let date = odt.date();
67 let time = odt.time();
68 let offset = odt.offset();
69
70 let year = date.year();
71
72 if !(0..10_000).contains(&year) {
73 return Err(fmt::Error);
74 }
75 if offset.whole_hours().unsigned_abs() > 23 {
76 return Err(fmt::Error);
77 }
78 if offset.seconds_past_minute() != 0 {
79 return Err(fmt::Error);
80 }
81
82 let month = u8::from(date.month());
83 let day = date.day();
84
85 let hour = time.hour();
86 let minute = time.minute();
87 let second = time.second();
88
89 let ns = time.nanosecond();
90
91 let subsecond = if ns == 0 {
92 ""
93 } else if ns % 10 != 0 {
94 &format!(".{:09}", ns)
95 } else if (ns / 10) % 10 != 0 {
96 &format!(".{:08}", ns / 10)
97 } else if (ns / 100) % 10 != 0 {
98 &format!(".{:07}", ns / 100)
99 } else if (ns / 1_000) % 10 != 0 {
100 &format!(".{:06}", ns / 1_000)
101 } else if (ns / 10_000) % 10 != 0 {
102 &format!(".{:05}", ns / 10_000)
103 } else if (ns / 100_000) % 10 != 0 {
104 &format!(".{:04}", ns / 100_000)
105 } else if (ns / 1_000_000) % 10 != 0 {
106 &format!(".{:03}", ns / 1_000_000)
107 } else if (ns / 10_000_000) % 10 != 0 {
108 &format!(".{:02}", ns / 10_000_000)
109 } else {
110 &format!(".{:01}", ns / 100_000_000)
111 };
112
113 let tz_h = offset.whole_hours().unsigned_abs();
114 let tz_m = offset.minutes_past_hour().unsigned_abs();
115
116 let tzs = if offset.is_utc() {
117 "Z"
118 } else if offset.is_negative() {
119 &format!("-{tz_h:02}:{tz_m:02}")
120 } else {
121 &format!("+{tz_h:02}:{tz_m:02}")
122 };
123
124 let s = format!(
125 "{year:02}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}{subsecond}{tzs}"
126 );
127
128 Ok(s)
129 })
130 .transpose()
131 .map_err(S::Error::custom)?
132 .serialize(serializer)
133 }
134}
135
136#[serde_as]
140#[skip_serializing_none]
141#[derive(Deserialize, Serialize)]
142#[serde(rename_all = "camelCase")]
143pub struct Thing<Other: ExtendableThing = Nil> {
144 #[serde(rename = "@context", default = "default_context")]
149 pub context: Value,
150
151 pub id: Option<String>,
153
154 #[serde(rename = "@type", default)]
156 #[serde_as(as = "Option<OneOrMany<_>>")]
157 pub attype: Option<Vec<String>>,
158
159 pub title: String,
161
162 pub titles: Option<MultiLanguage>,
164
165 pub description: Option<String>,
167
168 pub descriptions: Option<MultiLanguage>,
170
171 pub version: Option<VersionInfo>,
173
174 #[serde(with = "rfc3339_option", default)]
178 pub created: Option<OffsetDateTime>,
179
180 #[serde(with = "rfc3339_option", default)]
184 pub modified: Option<OffsetDateTime>,
185
186 pub support: Option<String>,
191
192 pub base: Option<String>,
197
198 pub properties: Option<HashMap<String, PropertyAffordance<Other>>>,
200
201 pub actions: Option<HashMap<String, ActionAffordance<Other>>>,
203
204 pub events: Option<HashMap<String, EventAffordance<Other>>>,
206
207 pub links: Option<Vec<Link>>,
211
212 pub forms: Option<Vec<Form<Other>>>,
214
215 #[serde_as(as = "OneOrMany<_>")]
220 pub security: Vec<String>,
221
222 pub security_definitions: HashMap<String, SecurityScheme>,
228
229 pub uri_variables: Option<DataSchemaMap<Other>>,
238
239 #[serde(default)]
244 #[serde_as(as = "Option<OneOrMany<_>>")]
245 pub profile: Option<Vec<String>>,
246
247 pub schema_definitions: Option<DataSchemaMap<Other>>,
251
252 #[serde(flatten)]
254 pub other: Other,
255}
256
257impl<Other> fmt::Debug for Thing<Other>
258where
259 Other: ExtendableThing + fmt::Debug,
260 PropertyAffordance<Other>: fmt::Debug,
261 ActionAffordance<Other>: fmt::Debug,
262 EventAffordance<Other>: fmt::Debug,
263 Form<Other>: fmt::Debug,
264 DataSchemaFromOther<Other>: fmt::Debug,
265{
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 f.debug_struct("Thing")
268 .field("context", &self.context)
269 .field("id", &self.id)
270 .field("attype", &self.attype)
271 .field("title", &self.title)
272 .field("titles", &self.titles)
273 .field("description", &self.description)
274 .field("descriptions", &self.descriptions)
275 .field("version", &self.version)
276 .field("created", &self.created)
277 .field("modified", &self.modified)
278 .field("support", &self.support)
279 .field("base", &self.base)
280 .field("properties", &self.properties)
281 .field("actions", &self.actions)
282 .field("events", &self.events)
283 .field("links", &self.links)
284 .field("forms", &self.forms)
285 .field("security", &self.security)
286 .field("security_definitions", &self.security_definitions)
287 .field("uri_variables", &self.uri_variables)
288 .field("profile", &self.profile)
289 .field("schema_definitions", &self.schema_definitions)
290 .field("other", &self.other)
291 .finish()
292 }
293}
294
295impl<Other> Default for Thing<Other>
296where
297 Other: ExtendableThing + Default,
298{
299 fn default() -> Self {
300 Self {
301 context: Default::default(),
302 id: Default::default(),
303 attype: Default::default(),
304 title: Default::default(),
305 titles: Default::default(),
306 description: Default::default(),
307 descriptions: Default::default(),
308 version: Default::default(),
309 created: Default::default(),
310 modified: Default::default(),
311 support: Default::default(),
312 base: Default::default(),
313 properties: Default::default(),
314 actions: Default::default(),
315 events: Default::default(),
316 links: Default::default(),
317 forms: Default::default(),
318 security: Default::default(),
319 security_definitions: Default::default(),
320 uri_variables: Default::default(),
321 profile: Default::default(),
322 schema_definitions: Default::default(),
323 other: Default::default(),
324 }
325 }
326}
327
328impl<Other> PartialEq for Thing<Other>
329where
330 Other: ExtendableThing + PartialEq,
331 Form<Other>: PartialEq,
332 PropertyAffordance<Other>: PartialEq,
333 ActionAffordance<Other>: PartialEq,
334 EventAffordance<Other>: PartialEq,
335 DataSchemaFromOther<Other>: PartialEq,
336{
337 fn eq(&self, other: &Self) -> bool {
338 self.context == other.context
339 && self.id == other.id
340 && self.attype == other.attype
341 && self.title == other.title
342 && self.titles == other.titles
343 && self.description == other.description
344 && self.descriptions == other.descriptions
345 && self.version == other.version
346 && self.created == other.created
347 && self.modified == other.modified
348 && self.support == other.support
349 && self.base == other.base
350 && self.properties == other.properties
351 && self.actions == other.actions
352 && self.events == other.events
353 && self.links == other.links
354 && self.forms == other.forms
355 && self.security == other.security
356 && self.security_definitions == other.security_definitions
357 && self.uri_variables == other.uri_variables
358 && self.profile == other.profile
359 && self.schema_definitions == other.schema_definitions
360 && self.other == other.other
361 }
362}
363
364fn default_context() -> Value {
365 TD_CONTEXT_11.into()
366}
367
368impl Thing<Nil> {
369 #[inline]
371 pub fn builder(title: impl Into<String>) -> ThingBuilder<Nil, ToExtend> {
372 ThingBuilder::new(title)
373 }
374}
375
376#[serde_as]
383#[skip_serializing_none]
384#[derive(Deserialize, Serialize)]
385#[serde(rename_all = "camelCase")]
386pub struct InteractionAffordance<Other: ExtendableThing> {
387 #[serde(rename = "@type", default)]
389 #[serde_as(as = "Option<OneOrMany<_>>")]
390 pub attype: Option<Vec<String>>,
391
392 pub title: Option<String>,
394
395 pub titles: Option<MultiLanguage>,
397
398 pub description: Option<String>,
400
401 pub descriptions: Option<MultiLanguage>,
403
404 pub forms: Vec<Form<Other>>,
406
407 pub uri_variables: Option<DataSchemaMap<Other>>,
414
415 #[serde(flatten)]
417 pub other: Other::InteractionAffordance,
418}
419
420impl<Other> fmt::Debug for InteractionAffordance<Other>
421where
422 Other: ExtendableThing,
423 Form<Other>: fmt::Debug,
424 DataSchemaFromOther<Other>: fmt::Debug,
425 Other::InteractionAffordance: fmt::Debug,
426{
427 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
428 f.debug_struct("InteractionAffordance")
429 .field("attype", &self.attype)
430 .field("title", &self.title)
431 .field("titles", &self.titles)
432 .field("description", &self.description)
433 .field("descriptions", &self.descriptions)
434 .field("forms", &self.forms)
435 .field("uri_variables", &self.uri_variables)
436 .field("other", &self.other)
437 .finish()
438 }
439}
440
441impl<Other> Default for InteractionAffordance<Other>
442where
443 Other: ExtendableThing,
444 Form<Other>: Default,
445 DataSchemaFromOther<Other>: Default,
446 Other::InteractionAffordance: Default,
447{
448 fn default() -> Self {
449 Self {
450 attype: Default::default(),
451 title: Default::default(),
452 titles: Default::default(),
453 description: Default::default(),
454 descriptions: Default::default(),
455 forms: Default::default(),
456 uri_variables: Default::default(),
457 other: Default::default(),
458 }
459 }
460}
461
462impl<Other> PartialEq for InteractionAffordance<Other>
463where
464 Other: ExtendableThing,
465 Form<Other>: PartialEq,
466 DataSchemaFromOther<Other>: PartialEq,
467 Other::InteractionAffordance: PartialEq,
468{
469 fn eq(&self, other: &Self) -> bool {
470 self.attype == other.attype
471 && self.title == other.title
472 && self.titles == other.titles
473 && self.description == other.description
474 && self.descriptions == other.descriptions
475 && self.forms == other.forms
476 && self.uri_variables == other.uri_variables
477 && self.other == other.other
478 }
479}
480
481fn omit_common<S, O>(i: &InteractionAffordance<O>, ser: S) -> Result<S::Ok, S::Error>
483where
484 S: Serializer,
485 O: ExtendableThing,
486{
487 use serde::ser::SerializeMap;
488
489 let mut s = ser.serialize_map(Some(3))?;
490 s.serialize_entry("forms", &i.forms)?;
491 if i.uri_variables.is_some() {
492 s.serialize_entry("uriVariables", &i.uri_variables)?;
493 }
494
495 Serialize::serialize(
496 &&i.other,
497 crate::flat_map_serialize::FlatMapSerializer(&mut s),
498 )?;
499
500 s.end()
501}
502
503#[skip_serializing_none]
508#[derive(Deserialize, Serialize)]
509pub struct PropertyAffordance<Other: ExtendableThing> {
510 #[serde(flatten)]
512 #[serde(serialize_with = "omit_common")]
513 pub interaction: InteractionAffordance<Other>,
514
515 #[serde(flatten)]
517 pub data_schema: DataSchemaFromOther<Other>,
518
519 pub observable: Option<bool>,
526
527 #[serde(flatten)]
529 pub other: Other::PropertyAffordance,
530}
531
532impl<Other> fmt::Debug for PropertyAffordance<Other>
533where
534 Other: ExtendableThing,
535 InteractionAffordance<Other>: fmt::Debug,
536 DataSchemaFromOther<Other>: fmt::Debug,
537 Other::PropertyAffordance: fmt::Debug,
538{
539 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
540 f.debug_struct("PropertyAffordance")
541 .field("interaction", &self.interaction)
542 .field("data_schema", &self.data_schema)
543 .field("observable", &self.observable)
544 .field("other", &self.other)
545 .finish()
546 }
547}
548
549impl<Other> Default for PropertyAffordance<Other>
550where
551 Other: ExtendableThing,
552 InteractionAffordance<Other>: Default,
553 DataSchemaFromOther<Other>: Default,
554 Other::PropertyAffordance: Default,
555{
556 fn default() -> Self {
557 Self {
558 interaction: Default::default(),
559 data_schema: Default::default(),
560 observable: Default::default(),
561 other: Default::default(),
562 }
563 }
564}
565
566impl<Other> PartialEq for PropertyAffordance<Other>
567where
568 Other: ExtendableThing,
569 InteractionAffordance<Other>: PartialEq,
570 DataSchemaFromOther<Other>: PartialEq,
571 Other::PropertyAffordance: PartialEq,
572{
573 fn eq(&self, other: &Self) -> bool {
574 self.interaction == other.interaction
575 && self.data_schema == other.data_schema
576 && self.observable == other.observable
577 && self.other == other.other
578 }
579}
580
581#[skip_serializing_none]
583#[derive(Deserialize, Serialize)]
584pub struct ActionAffordance<Other: ExtendableThing> {
585 #[serde(flatten)]
587 pub interaction: InteractionAffordance<Other>,
588
589 pub input: Option<DataSchemaFromOther<Other>>,
591
592 pub output: Option<DataSchemaFromOther<Other>>,
594
595 #[serde(default)]
600 pub safe: bool,
601
602 #[serde(default)]
607 pub idempotent: bool,
608
609 pub synchronous: Option<bool>,
616
617 #[serde(flatten)]
619 pub other: Other::ActionAffordance,
620}
621
622impl<Other> fmt::Debug for ActionAffordance<Other>
623where
624 Other: ExtendableThing,
625 InteractionAffordance<Other>: fmt::Debug,
626 DataSchemaFromOther<Other>: fmt::Debug,
627 Other::ActionAffordance: fmt::Debug,
628{
629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630 f.debug_struct("ActionAffordance")
631 .field("interaction", &self.interaction)
632 .field("input", &self.input)
633 .field("output", &self.output)
634 .field("safe", &self.safe)
635 .field("idempotent", &self.idempotent)
636 .field("synchronous", &self.synchronous)
637 .field("other", &self.other)
638 .finish()
639 }
640}
641
642impl<Other> Default for ActionAffordance<Other>
643where
644 Other: ExtendableThing,
645 InteractionAffordance<Other>: Default,
646 DataSchemaFromOther<Other>: Default,
647 Other::ActionAffordance: Default,
648{
649 fn default() -> Self {
650 Self {
651 interaction: Default::default(),
652 input: Default::default(),
653 output: Default::default(),
654 safe: Default::default(),
655 idempotent: Default::default(),
656 synchronous: Default::default(),
657 other: Default::default(),
658 }
659 }
660}
661
662impl<Other> PartialEq for ActionAffordance<Other>
663where
664 Other: ExtendableThing,
665 InteractionAffordance<Other>: PartialEq,
666 DataSchemaFromOther<Other>: PartialEq,
667 Other::ActionAffordance: PartialEq,
668{
669 fn eq(&self, other: &Self) -> bool {
670 self.interaction == other.interaction
671 && self.input == other.input
672 && self.output == other.output
673 && self.safe == other.safe
674 && self.idempotent == other.idempotent
675 && self.synchronous == other.synchronous
676 && self.other == other.other
677 }
678}
679
680#[skip_serializing_none]
682#[derive(Deserialize, Serialize)]
683pub struct EventAffordance<Other: ExtendableThing> {
684 #[serde(flatten)]
686 pub interaction: InteractionAffordance<Other>,
687
688 pub subscription: Option<DataSchemaFromOther<Other>>,
690
691 pub data: Option<DataSchemaFromOther<Other>>,
693
694 pub data_response: Option<DataSchemaFromOther<Other>>,
696
697 pub cancellation: Option<DataSchemaFromOther<Other>>,
699
700 #[serde(flatten)]
702 pub other: Other::EventAffordance,
703}
704
705impl<Other> fmt::Debug for EventAffordance<Other>
706where
707 Other: ExtendableThing,
708 InteractionAffordance<Other>: fmt::Debug,
709 DataSchemaFromOther<Other>: fmt::Debug,
710 Other::EventAffordance: fmt::Debug,
711{
712 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
713 f.debug_struct("EventAffordance")
714 .field("interaction", &self.interaction)
715 .field("subscription", &self.subscription)
716 .field("data", &self.data)
717 .field("data_response", &self.data_response)
718 .field("cancellation", &self.cancellation)
719 .field("other", &self.other)
720 .finish()
721 }
722}
723
724impl<Other> Default for EventAffordance<Other>
725where
726 Other: ExtendableThing,
727 InteractionAffordance<Other>: Default,
728 DataSchemaFromOther<Other>: Default,
729 Other::EventAffordance: Default,
730{
731 fn default() -> Self {
732 Self {
733 interaction: Default::default(),
734 subscription: Default::default(),
735 data: Default::default(),
736 data_response: Default::default(),
737 cancellation: Default::default(),
738 other: Default::default(),
739 }
740 }
741}
742
743impl<Other> PartialEq for EventAffordance<Other>
744where
745 Other: ExtendableThing,
746 InteractionAffordance<Other>: PartialEq,
747 DataSchemaFromOther<Other>: PartialEq,
748 Other::EventAffordance: PartialEq,
749{
750 fn eq(&self, other: &Self) -> bool {
751 self.interaction == other.interaction
752 && self.subscription == other.subscription
753 && self.data == other.data
754 && self.data_response == other.data_response
755 && self.cancellation == other.cancellation
756 && self.other == other.other
757 }
758}
759
760#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
762pub struct VersionInfo {
763 pub instance: String,
765
766 pub model: Option<String>,
768}
769
770impl<S> From<S> for VersionInfo
771where
772 S: Into<String>,
773{
774 fn from(instance: S) -> Self {
775 let instance = instance.into();
776 Self {
777 instance,
778 model: None,
779 }
780 }
781}
782
783#[serde_as]
785#[skip_serializing_none]
786#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
787#[serde(rename_all = "camelCase")]
788pub struct DataSchema<DS, AS, OS> {
789 #[serde(rename = "@type", default)]
791 #[serde_as(as = "Option<OneOrMany<_>>")]
792 pub attype: Option<Vec<String>>,
793
794 pub title: Option<String>,
796
797 pub titles: Option<MultiLanguage>,
799
800 pub description: Option<String>,
802
803 pub descriptions: Option<MultiLanguage>,
805
806 #[serde(rename = "const")]
808 pub constant: Option<Value>,
809
810 pub default: Option<Value>,
812
813 pub unit: Option<String>,
815
816 pub one_of: Option<Vec<Self>>,
818
819 #[serde(rename = "enum")]
821 pub enumeration: Option<Vec<Value>>,
822
823 #[serde(default)]
825 pub read_only: bool,
826
827 #[serde(default)]
829 pub write_only: bool,
830
831 pub format: Option<String>,
833
834 #[serde(flatten)]
836 pub subtype: Option<DataSchemaSubtype<DS, AS, OS>>,
837
838 #[serde(flatten)]
840 pub other: DS,
841}
842
843pub(crate) type DataSchemaFromOther<Other> = DataSchema<
844 <Other as ExtendableThing>::DataSchema,
845 <Other as ExtendableThing>::ArraySchema,
846 <Other as ExtendableThing>::ObjectSchema,
847>;
848
849#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
851#[serde(tag = "type", rename_all = "lowercase")]
852pub enum DataSchemaSubtype<DS, AS, OS> {
853 Array(ArraySchema<DS, AS, OS>),
855
856 Boolean,
858
859 Number(NumberSchema),
861
862 Integer(IntegerSchema),
864
865 Object(ObjectSchema<DS, AS, OS>),
867
868 String(StringSchema),
870
871 Null,
873}
874
875#[derive(Clone, Debug, PartialEq)]
876pub(crate) enum UncheckedDataSchemaSubtype<DS, AS, OS> {
877 Array(UncheckedArraySchema<DS, AS, OS>),
878 Boolean,
879 Number(NumberSchema),
880 Integer(IntegerSchema),
881 Object(UncheckedObjectSchema<DS, AS, OS>),
882 String(StringSchema),
883 Null,
884}
885
886impl<DS, AS, OS> Default for DataSchemaSubtype<DS, AS, OS> {
887 fn default() -> Self {
888 Self::Null
889 }
890}
891
892#[serde_as]
894#[skip_serializing_none]
895#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
896#[serde(bound(
897 deserialize = "DS: Deserialize<'de>, AS: Deserialize<'de>, OS: Deserialize<'de>",
898 serialize = "DS: Serialize, AS: Serialize, OS: Serialize"
899))]
900pub struct ArraySchema<DS, AS, OS> {
901 #[serde(skip_serializing_if = "Option::is_none")]
913 pub items: Option<BoxedElemOrVec<DataSchema<DS, AS, OS>>>,
914
915 pub min_items: Option<u32>,
917
918 pub max_items: Option<u32>,
920
921 #[serde(flatten)]
923 pub other: AS,
924}
925
926#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
927#[serde(untagged)]
928pub enum BoxedElemOrVec<T> {
929 Elem(Box<T>),
930 Vec(Vec<T>),
931}
932
933#[derive(Clone, Debug, Default, PartialEq)]
934pub(crate) struct UncheckedArraySchema<DS, AS, OS> {
935 pub(crate) items: Option<BoxedElemOrVec<UncheckedDataSchema<DS, AS, OS>>>,
936 pub(crate) min_items: Option<u32>,
937 pub(crate) max_items: Option<u32>,
938 pub(crate) other: AS,
939}
940
941impl<DS, AS, OS> Default for ArraySchema<DS, AS, OS>
942where
943 AS: Default,
944{
945 fn default() -> Self {
946 Self {
947 items: Default::default(),
948 min_items: Default::default(),
949 max_items: Default::default(),
950 other: Default::default(),
951 }
952 }
953}
954
955#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
957pub enum Maximum<T> {
958 #[serde(rename = "maximum")]
960 Inclusive(T),
961
962 #[serde(rename = "exclusiveMaximum")]
964 Exclusive(T),
965}
966
967#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
969pub enum Minimum<T> {
970 #[serde(rename = "minimum")]
972 Inclusive(T),
973
974 #[serde(rename = "exclusiveMinimum")]
976 Exclusive(T),
977}
978
979impl<T> PartialOrd for Minimum<T>
980where
981 T: PartialOrd,
982{
983 #[inline]
984 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
985 match (self, other) {
986 (Minimum::Inclusive(a), Minimum::Inclusive(b))
987 | (Minimum::Exclusive(a), Minimum::Exclusive(b)) => a.partial_cmp(b),
988 (Minimum::Inclusive(a), Minimum::Exclusive(b)) => {
989 a.partial_cmp(b).and_then(|ord| match ord {
990 Ordering::Less | Ordering::Equal => Some(Ordering::Less),
991 Ordering::Greater => None,
992 })
993 }
994 (Minimum::Exclusive(a), Minimum::Inclusive(b)) => {
995 a.partial_cmp(b).and_then(|ord| match ord {
996 Ordering::Less => None,
997 Ordering::Equal | Ordering::Greater => Some(Ordering::Greater),
998 })
999 }
1000 }
1001 }
1002}
1003
1004impl<T> PartialOrd for Maximum<T>
1005where
1006 T: PartialOrd,
1007{
1008 #[inline]
1009 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1010 match (self, other) {
1011 (Maximum::Inclusive(a), Maximum::Inclusive(b))
1012 | (Maximum::Exclusive(a), Maximum::Exclusive(b)) => a.partial_cmp(b),
1013
1014 (Maximum::Inclusive(a), Maximum::Exclusive(b)) => {
1015 a.partial_cmp(b).and_then(|ord| match ord {
1016 Ordering::Less => None,
1017 Ordering::Equal | Ordering::Greater => Some(Ordering::Greater),
1018 })
1019 }
1020
1021 (Maximum::Exclusive(a), Maximum::Inclusive(b)) => {
1022 a.partial_cmp(b).and_then(|ord| match ord {
1023 Ordering::Less | Ordering::Equal => Some(Ordering::Less),
1024 Ordering::Greater => None,
1025 })
1026 }
1027 }
1028 }
1029}
1030
1031impl<T> PartialEq<Maximum<T>> for Minimum<T>
1032where
1033 T: PartialEq,
1034{
1035 #[inline]
1036 fn eq(&self, other: &Maximum<T>) -> bool {
1037 match (self, other) {
1038 (Minimum::Inclusive(a), Maximum::Inclusive(b))
1039 | (Minimum::Exclusive(a), Maximum::Exclusive(b)) => a == b,
1040 _ => false,
1041 }
1042 }
1043}
1044
1045impl<T> PartialEq<Minimum<T>> for Maximum<T>
1046where
1047 T: PartialEq,
1048{
1049 #[inline]
1050 fn eq(&self, other: &Minimum<T>) -> bool {
1051 other == self
1052 }
1053}
1054
1055impl<T> PartialOrd<Maximum<T>> for Minimum<T>
1056where
1057 T: PartialOrd,
1058{
1059 #[inline]
1060 fn partial_cmp(&self, other: &Maximum<T>) -> Option<cmp::Ordering> {
1061 match (self, other) {
1062 (Minimum::Inclusive(a), Maximum::Inclusive(b))
1063 | (Minimum::Exclusive(a), Maximum::Exclusive(b)) => a.partial_cmp(b),
1064
1065 (Minimum::Exclusive(a), Maximum::Inclusive(b))
1066 | (Minimum::Inclusive(a), Maximum::Exclusive(b)) => {
1067 a.partial_cmp(b).and_then(|ord| match ord {
1068 Ordering::Less => None,
1069 Ordering::Equal | Ordering::Greater => Some(Ordering::Greater),
1070 })
1071 }
1072 }
1073 }
1074}
1075
1076impl<T> PartialOrd<Minimum<T>> for Maximum<T>
1077where
1078 T: PartialOrd,
1079{
1080 #[inline]
1081 fn partial_cmp(&self, other: &Minimum<T>) -> Option<Ordering> {
1082 other.partial_cmp(self).map(Ordering::reverse)
1083 }
1084}
1085
1086macro_rules! impl_minmax_float {
1087 (@ $ty:ident $float_type:ty) => {
1088 impl $ty<$float_type> {
1089 pub fn is_nan(&self) -> bool {
1091 match self {
1092 Self::Inclusive(x) => x.is_nan(),
1093 Self::Exclusive(x) => x.is_nan(),
1094 }
1095 }
1096 }
1097 };
1098
1099 ($($float_type:ty),*) => {
1100 $(
1101 impl_minmax_float!(@ Minimum $float_type);
1102 impl_minmax_float!(@ Maximum $float_type);
1103 )*
1104 };
1105}
1106
1107impl_minmax_float!(f32, f64);
1108
1109#[skip_serializing_none]
1111#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
1112#[serde(rename_all = "camelCase")]
1113pub struct NumberSchema {
1114 #[serde(flatten)]
1116 pub maximum: Option<Maximum<f64>>,
1117
1118 #[serde(flatten)]
1120 pub minimum: Option<Minimum<f64>>,
1121
1122 pub multiple_of: Option<f64>,
1124}
1125
1126#[skip_serializing_none]
1128#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1129#[serde(rename_all = "camelCase")]
1130pub struct IntegerSchema {
1132 #[serde(flatten)]
1134 pub maximum: Option<Maximum<i64>>,
1135
1136 #[serde(flatten)]
1138 pub minimum: Option<Minimum<i64>>,
1139
1140 pub multiple_of: Option<NonZeroU64>,
1142}
1143
1144#[skip_serializing_none]
1146#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1147pub struct ObjectSchema<DS, AS, OS> {
1148 pub properties: Option<HashMap<String, DataSchema<DS, AS, OS>>>,
1150
1151 pub required: Option<Vec<String>>,
1153
1154 #[serde(flatten)]
1156 pub other: OS,
1157}
1158
1159#[derive(Clone, Debug, PartialEq)]
1160pub(crate) struct UncheckedObjectSchema<DS, AS, OS> {
1161 pub(crate) properties: Option<HashMap<String, UncheckedDataSchema<DS, AS, OS>>>,
1162 pub(crate) required: Option<Vec<String>>,
1163 pub(crate) other: OS,
1164}
1165
1166impl<DS, AS, OS> Default for ObjectSchema<DS, AS, OS>
1167where
1168 OS: Default,
1169{
1170 fn default() -> Self {
1171 Self {
1172 properties: Default::default(),
1173 required: Default::default(),
1174 other: Default::default(),
1175 }
1176 }
1177}
1178
1179impl<DS, AS, OS> Default for UncheckedObjectSchema<DS, AS, OS>
1180where
1181 OS: Default,
1182{
1183 fn default() -> Self {
1184 Self {
1185 properties: Default::default(),
1186 required: Default::default(),
1187 other: Default::default(),
1188 }
1189 }
1190}
1191
1192#[skip_serializing_none]
1194#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1195#[serde(rename_all = "camelCase")]
1196pub struct StringSchema {
1197 pub min_length: Option<u32>,
1199
1200 pub max_length: Option<u32>,
1202
1203 pub pattern: Option<String>,
1208
1209 pub content_encoding: Option<String>,
1213
1214 pub content_media_type: Option<String>,
1218}
1219
1220#[serde_as]
1222#[skip_serializing_none]
1223#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1224pub struct SecurityScheme {
1225 #[serde(rename = "@type", default)]
1227 #[serde_as(as = "Option<OneOrMany<_>>")]
1228 pub attype: Option<Vec<String>>,
1229
1230 pub description: Option<String>,
1232
1233 pub descriptions: Option<MultiLanguage>,
1235
1236 pub proxy: Option<String>,
1240
1241 #[serde(flatten)]
1243 pub subtype: SecuritySchemeSubtype,
1244}
1245
1246#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1248#[serde(tag = "scheme", rename_all = "lowercase")]
1249pub enum KnownSecuritySchemeSubtype {
1250 #[default]
1252 NoSec,
1253
1254 Auto,
1256
1257 Combo(ComboSecurityScheme),
1259
1260 Basic(BasicSecurityScheme),
1263
1264 Digest(DigestSecurityScheme),
1267
1268 Bearer(BearerSecurityScheme),
1270
1271 Psk(PskSecurityScheme),
1273
1274 OAuth2(OAuth2SecurityScheme),
1279
1280 ApiKey(ApiKeySecurityScheme),
1282}
1283
1284#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1286pub struct UnknownSecuritySchemeSubtype {
1287 pub scheme: String,
1289
1290 #[serde(flatten)]
1292 pub data: Value,
1293}
1294
1295#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1297#[serde(untagged)]
1298pub enum SecuritySchemeSubtype {
1299 Known(KnownSecuritySchemeSubtype),
1301
1302 Unknown(UnknownSecuritySchemeSubtype),
1304}
1305
1306impl Default for SecuritySchemeSubtype {
1307 fn default() -> Self {
1308 Self::Known(KnownSecuritySchemeSubtype::default())
1309 }
1310}
1311
1312#[serde_as]
1314#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1315#[serde(rename_all = "camelCase")]
1316pub enum ComboSecurityScheme {
1317 OneOf(#[serde_as(as = "OneOrMany<_>")] Vec<String>),
1320
1321 AllOf(#[serde_as(as = "OneOrMany<_>")] Vec<String>),
1324}
1325
1326#[skip_serializing_none]
1328#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1329pub struct BasicSecurityScheme {
1330 #[serde(rename = "in", default = "SecurityAuthenticationLocation::header")]
1332 pub location: SecurityAuthenticationLocation,
1333
1334 pub name: Option<String>,
1336}
1337
1338impl Default for BasicSecurityScheme {
1339 fn default() -> Self {
1340 Self {
1341 location: SecurityAuthenticationLocation::Header,
1342 name: Default::default(),
1343 }
1344 }
1345}
1346
1347#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1349#[serde(rename_all = "lowercase")]
1350pub enum SecurityAuthenticationLocation {
1351 Header,
1354
1355 Query,
1358
1359 Body,
1362
1363 Cookie,
1365
1366 Uri,
1369}
1370
1371impl SecurityAuthenticationLocation {
1372 const fn header() -> Self {
1373 Self::Header
1374 }
1375
1376 const fn query() -> Self {
1377 Self::Query
1378 }
1379}
1380
1381#[skip_serializing_none]
1383#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1384pub struct DigestSecurityScheme {
1385 pub qop: QualityOfProtection,
1387
1388 #[serde(rename = "in", default = "SecurityAuthenticationLocation::header")]
1390 pub location: SecurityAuthenticationLocation,
1391
1392 pub name: Option<String>,
1394}
1395
1396impl Default for DigestSecurityScheme {
1397 fn default() -> Self {
1398 Self {
1399 qop: Default::default(),
1400 location: SecurityAuthenticationLocation::Header,
1401 name: Default::default(),
1402 }
1403 }
1404}
1405
1406#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1408#[serde(rename_all = "kebab-case")]
1409pub enum QualityOfProtection {
1410 #[default]
1412 Auth,
1413
1414 AuthInt,
1416}
1417
1418#[skip_serializing_none]
1420#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1421pub struct ApiKeySecurityScheme {
1422 #[serde(rename = "in", default = "SecurityAuthenticationLocation::query")]
1424 pub location: SecurityAuthenticationLocation,
1425
1426 pub name: Option<String>,
1428}
1429
1430impl Default for ApiKeySecurityScheme {
1431 fn default() -> Self {
1432 Self {
1433 location: SecurityAuthenticationLocation::Query,
1434 name: Default::default(),
1435 }
1436 }
1437}
1438
1439#[skip_serializing_none]
1441#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1442pub struct BearerSecurityScheme {
1443 pub authorization: Option<String>,
1446
1447 #[serde(default = "BearerSecurityScheme::default_alg")]
1449 pub alg: Cow<'static, str>,
1450
1451 #[serde(default = "BearerSecurityScheme::default_format")]
1453 pub format: Cow<'static, str>,
1454
1455 #[serde(rename = "in", default = "SecurityAuthenticationLocation::header")]
1457 pub location: SecurityAuthenticationLocation,
1458
1459 pub name: Option<String>,
1461}
1462
1463impl Default for BearerSecurityScheme {
1464 fn default() -> Self {
1465 Self {
1466 authorization: Default::default(),
1467 alg: BearerSecurityScheme::default_alg(),
1468 format: BearerSecurityScheme::default_format(),
1469 location: SecurityAuthenticationLocation::Header,
1470 name: Default::default(),
1471 }
1472 }
1473}
1474
1475impl BearerSecurityScheme {
1476 const fn default_alg() -> Cow<'static, str> {
1477 Cow::Borrowed("ES256")
1478 }
1479
1480 const fn default_format() -> Cow<'static, str> {
1481 Cow::Borrowed("jwt")
1482 }
1483}
1484
1485#[skip_serializing_none]
1487#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1488pub struct PskSecurityScheme {
1489 pub identity: Option<String>,
1491}
1492
1493#[serde_as]
1498#[skip_serializing_none]
1499#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1500pub struct OAuth2SecurityScheme {
1501 pub authorization: Option<String>,
1506
1507 pub token: Option<String>,
1510
1511 pub refresh: Option<String>,
1514
1515 #[serde(default)]
1520 #[serde_as(as = "Option<OneOrMany<_>>")]
1521 pub scopes: Option<Vec<String>>,
1522
1523 pub flow: String,
1525}
1526
1527impl OAuth2SecurityScheme {
1528 pub fn new(flow: impl Into<String>) -> Self {
1530 let flow = flow.into();
1531 Self {
1532 authorization: Default::default(),
1533 token: Default::default(),
1534 refresh: Default::default(),
1535 scopes: Default::default(),
1536 flow,
1537 }
1538 }
1539}
1540
1541#[serde_as]
1543#[skip_serializing_none]
1544#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1545pub struct Link {
1546 pub href: String,
1548
1549 #[serde(rename = "type")]
1553 pub ty: Option<String>,
1554
1555 pub rel: Option<String>,
1557
1558 pub anchor: Option<String>,
1563
1564 pub sizes: Option<String>,
1568
1569 #[serde(default)]
1571 #[serde_as(as = "Option<OneOrMany<_>>")]
1572 pub hreflang: Option<Vec<LanguageTag<String>>>,
1573}
1574
1575#[serde_as]
1577#[skip_serializing_none]
1578#[derive(Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1579#[serde(rename_all = "camelCase")]
1580pub struct Form<Other: ExtendableThing> {
1581 #[serde(default, skip_serializing_if = "DefaultedFormOperations::is_default")]
1583 pub op: DefaultedFormOperations,
1584
1585 pub href: String,
1588
1589 pub content_type: Option<String>,
1594
1595 pub content_coding: Option<String>,
1606
1607 pub subprotocol: Option<String>,
1610
1611 #[serde(default)]
1616 #[serde_as(as = "Option<OneOrMany<_>>")]
1617 pub security: Option<Vec<String>>,
1618
1619 #[serde(default)]
1624 #[serde_as(as = "Option<OneOrMany<_>>")]
1625 pub scopes: Option<Vec<String>>,
1626
1627 pub response: Option<ExpectedResponse<Other::ExpectedResponse>>,
1631
1632 #[serde(default)]
1634 #[serde_as(as = "Option<OneOrMany<_>>")]
1635 pub additional_responses: Option<Vec<AdditionalExpectedResponse>>,
1636
1637 #[serde(flatten)]
1639 pub other: Other::Form,
1640}
1641
1642impl<Other> Clone for Form<Other>
1643where
1644 Other: ExtendableThing,
1645 Other::ExpectedResponse: Clone,
1646 Other::Form: Clone,
1647{
1648 fn clone(&self) -> Self {
1649 Self {
1650 op: self.op.clone(),
1651 href: self.href.clone(),
1652 content_type: self.content_type.clone(),
1653 content_coding: self.content_coding.clone(),
1654 subprotocol: self.subprotocol.clone(),
1655 security: self.security.clone(),
1656 scopes: self.scopes.clone(),
1657 response: self.response.clone(),
1658 additional_responses: self.additional_responses.clone(),
1659 other: self.other.clone(),
1660 }
1661 }
1662}
1663
1664#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1666#[serde(rename_all = "lowercase")]
1667pub enum FormOperation {
1668 ReadProperty,
1670
1671 WriteProperty,
1673
1674 ObserveProperty,
1678
1679 UnobserveProperty,
1683
1684 InvokeAction,
1686
1687 QueryAction,
1689
1690 CancelAction,
1692
1693 SubscribeEvent,
1697
1698 UnsubscribeEvent,
1702
1703 ReadAllProperties,
1705
1706 WriteAllProperties,
1708
1709 ReadMultipleProperties,
1711
1712 WriteMultipleProperties,
1714
1715 ObserveAllProperties,
1717
1718 UnobserveAllProperties,
1720
1721 SubscribeAllEvents,
1723
1724 UnsubscribeAllEvents,
1726
1727 QueryAllActions,
1729}
1730
1731impl fmt::Display for FormOperation {
1732 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1733 let s = match self {
1734 Self::ReadProperty => "readproperty",
1735 Self::WriteProperty => "writeproperty",
1736 Self::ObserveProperty => "observeproperty",
1737 Self::UnobserveProperty => "unobserveproperty",
1738 Self::InvokeAction => "invokeaction",
1739 Self::QueryAction => "queryaction",
1740 Self::CancelAction => "cancelaction",
1741 Self::SubscribeEvent => "subscribeevent",
1742 Self::UnsubscribeEvent => "unsubscribeevent",
1743 Self::ReadAllProperties => "readallproperties",
1744 Self::WriteAllProperties => "writeallproperties",
1745 Self::ReadMultipleProperties => "readmultipleproperties",
1746 Self::WriteMultipleProperties => "writemultipleproperties",
1747 Self::ObserveAllProperties => "observeallproperties",
1748 Self::UnobserveAllProperties => "unobserveallproperties",
1749 Self::SubscribeAllEvents => "subscribeallevents",
1750 Self::UnsubscribeAllEvents => "unsubscribeallevents",
1751 Self::QueryAllActions => "queryallactions",
1752 };
1753
1754 f.write_str(s)
1755 }
1756}
1757
1758#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
1765pub enum DefaultedFormOperations {
1766 #[default]
1768 Default,
1769
1770 Custom(Vec<FormOperation>),
1772}
1773
1774impl DefaultedFormOperations {
1775 #[inline]
1777 pub const fn is_default(&self) -> bool {
1778 matches!(self, Self::Default)
1779 }
1780}
1781
1782impl Serialize for DefaultedFormOperations {
1783 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1784 where
1785 S: Serializer,
1786 {
1787 match self {
1788 Self::Default => serializer.serialize_none(),
1789 Self::Custom(ops) if ops.is_empty() => serializer.serialize_none(),
1790 Self::Custom(ops) => ops.serialize(serializer),
1791 }
1792 }
1793}
1794
1795impl<'de> Deserialize<'de> for DefaultedFormOperations
1796where
1797 OneOrMany<Same>: DeserializeAs<'de, Vec<FormOperation>>,
1798{
1799 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1800 where
1801 D: Deserializer<'de>,
1802 {
1803 let ops = Option::<OneOrMany<_>>::deserialize_as(deserializer)?;
1804 Ok(ops.map(Self::Custom).unwrap_or(Self::Default))
1805 }
1806}
1807
1808#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1810#[serde(rename_all = "camelCase")]
1811pub struct ExpectedResponse<Other> {
1812 pub content_type: String,
1817
1818 #[serde(flatten)]
1820 pub other: Other,
1821}
1822
1823#[skip_serializing_none]
1825#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1826#[serde(rename_all = "camelCase")]
1827pub struct AdditionalExpectedResponse {
1828 #[serde(default = "bool_false", skip_serializing_if = "is_false")]
1830 pub success: bool,
1831
1832 pub content_type: Option<String>,
1837
1838 pub schema: Option<String>,
1843}
1844
1845const fn bool_false() -> bool {
1846 false
1847}
1848
1849const fn is_false(b: &bool) -> bool {
1850 !*b
1851}
1852
1853#[cfg(test)]
1854mod test {
1855 use alloc::vec;
1856
1857 use serde_json::json;
1858 use time::macros::datetime;
1859
1860 use crate::hlist::Cons;
1861
1862 use super::*;
1863
1864 use pretty_assertions::assert_eq;
1865
1866 #[test]
1867 fn minimal_thing() {
1868 const RAW: &str = r#"
1869 {
1870 "@context": "https://www.w3.org/2022/wot/td/v1.1",
1871 "id": "urn:dev:ops:32473-WoTLamp-1234",
1872 "title": "MyLampThing",
1873 "securityDefinitions": {
1874 "nosec": {"scheme": "nosec"}
1875 },
1876 "security": ["nosec"]
1877 }"#;
1878
1879 let expected_thing = Thing {
1880 context: TD_CONTEXT_11.into(),
1881 id: Some("urn:dev:ops:32473-WoTLamp-1234".to_string()),
1882 title: "MyLampThing".to_string(),
1883 security_definitions: [("nosec".to_string(), SecurityScheme::default())]
1884 .into_iter()
1885 .collect(),
1886 security: vec!["nosec".to_string()],
1887 ..Default::default()
1888 };
1889
1890 let thing: Thing = serde_json::from_str(RAW).unwrap();
1891 assert_eq!(thing, expected_thing);
1892
1893 let thing: Thing = serde_json::from_value(serde_json::to_value(thing).unwrap()).unwrap();
1894 assert_eq!(thing, expected_thing);
1895 }
1896
1897 #[test]
1898 fn complete_thing() {
1899 const RAW: &str = r#"
1900 {
1901 "@context": "https://www.w3.org/2022/wot/td/v1.1",
1902 "id": "urn:dev:ops:32473-WoTLamp-1234",
1903 "@type": [
1904 "Thing",
1905 "LampThing"
1906 ],
1907 "title": "MyLampThing",
1908 "titles": {
1909 "en": "MyLampThing",
1910 "it": "La mia lampada intelligente"
1911 },
1912 "description": "A simple smart lamp",
1913 "descriptions": {
1914 "en": "A simple smart lamp",
1915 "it": "Una semplice lampada intelligente"
1916 },
1917 "version": {
1918 "instance": "0.1.0",
1919 "model": "model"
1920 },
1921 "created": "2022-05-01T10:20:42.123Z",
1922 "modified": "2022-05-10T12:30:00.000+01:00",
1923 "support": "mailto:mail@test.com",
1924 "base": "https://mylamp.example.com/",
1925 "properties": {
1926 "status": {
1927 "type": "string",
1928 "forms": [
1929 {
1930 "href": "https://mylamp.example.com/status"
1931 }
1932 ]
1933 }
1934 },
1935 "actions": {
1936 "toggle": {
1937 "forms": [
1938 {
1939 "href": "https://mylamp.example.com/toggle"
1940 }
1941 ],
1942 "synchronous": false
1943 }
1944 },
1945 "events": {
1946 "overheating": {
1947 "data": {
1948 "type": "string"
1949 },
1950 "forms": [
1951 {
1952 "href": "https://mylamp.example.com/oh",
1953 "subprotocol": "longpoll"
1954 }
1955 ]
1956 }
1957 },
1958 "links": [
1959 {
1960 "href": "https://myswitch.example.com/"
1961 }
1962 ],
1963 "forms": [
1964 {
1965 "href": "https://mylamp.example.com/enumerate",
1966 "op": "readallproperties"
1967 }
1968 ],
1969 "schemaDefinitions": {
1970 "schema": {
1971 "type": "null"
1972 }
1973 },
1974 "securityDefinitions": {
1975 "nosec": {
1976 "scheme": "nosec"
1977 }
1978 },
1979 "security": [
1980 "nosec"
1981 ],
1982 "profile": [
1983 "profile1",
1984 "profile2"
1985 ],
1986 "uriVariables": {
1987 "uriVariable1": {
1988 "type": "string"
1989 },
1990 "uriVariable2": {
1991 "type": "number"
1992 }
1993 }
1994 }"#;
1995
1996 let expected_thing = Thing {
1997 context: TD_CONTEXT_11.into(),
1998 id: Some("urn:dev:ops:32473-WoTLamp-1234".to_string()),
1999 attype: Some(vec!["Thing".to_string(), "LampThing".to_string()]),
2000 title: "MyLampThing".to_string(),
2001 titles: Some(
2002 [
2003 ("en".parse().unwrap(), "MyLampThing".to_string()),
2004 (
2005 "it".parse().unwrap(),
2006 "La mia lampada intelligente".to_string(),
2007 ),
2008 ]
2009 .into_iter()
2010 .collect(),
2011 ),
2012 description: Some("A simple smart lamp".to_string()),
2013 descriptions: Some(
2014 [
2015 ("en".parse().unwrap(), "A simple smart lamp".to_string()),
2016 (
2017 "it".parse().unwrap(),
2018 "Una semplice lampada intelligente".to_string(),
2019 ),
2020 ]
2021 .into_iter()
2022 .collect(),
2023 ),
2024 version: Some(VersionInfo {
2025 instance: "0.1.0".to_string(),
2026 model: Some("model".to_string()),
2027 }),
2028 created: Some(datetime!(2022-05-01 10:20:42.123 UTC)),
2029 modified: Some(datetime!(2022-05-10 12:30 +1)),
2030 support: Some("mailto:mail@test.com".to_string()),
2031 base: Some("https://mylamp.example.com/".to_string()),
2032 properties: Some(
2033 [(
2034 "status".to_string(),
2035 PropertyAffordance {
2036 interaction: InteractionAffordance {
2037 forms: vec![Form {
2038 href: "https://mylamp.example.com/status".to_string(),
2039 ..Form::default()
2040 }],
2041 ..Default::default()
2042 },
2043 data_schema: DataSchema {
2044 subtype: Some(DataSchemaSubtype::String(Default::default())),
2045 ..Default::default()
2046 },
2047 ..Default::default()
2048 },
2049 )]
2050 .into_iter()
2051 .collect(),
2052 ),
2053 actions: Some(
2054 [(
2055 "toggle".to_string(),
2056 ActionAffordance {
2057 interaction: InteractionAffordance {
2058 forms: vec![Form {
2059 href: "https://mylamp.example.com/toggle".to_string(),
2060 ..Default::default()
2061 }],
2062 ..Default::default()
2063 },
2064 synchronous: Some(false),
2065 ..Default::default()
2066 },
2067 )]
2068 .into_iter()
2069 .collect(),
2070 ),
2071 events: Some(
2072 [(
2073 "overheating".to_string(),
2074 EventAffordance {
2075 interaction: InteractionAffordance {
2076 forms: vec![Form {
2077 href: "https://mylamp.example.com/oh".to_string(),
2078 subprotocol: Some("longpoll".to_string()),
2079 ..Default::default()
2080 }],
2081 ..Default::default()
2082 },
2083 data: Some(DataSchema {
2084 subtype: Some(DataSchemaSubtype::String(StringSchema::default())),
2085 ..Default::default()
2086 }),
2087 ..Default::default()
2088 },
2089 )]
2090 .into_iter()
2091 .collect(),
2092 ),
2093 links: Some(vec![Link {
2094 href: "https://myswitch.example.com/".to_string(),
2095 ..Default::default()
2096 }]),
2097 forms: Some(vec![Form {
2098 op: DefaultedFormOperations::Custom(vec![FormOperation::ReadAllProperties]),
2099 href: "https://mylamp.example.com/enumerate".to_string(),
2100 ..Default::default()
2101 }]),
2102 schema_definitions: Some(
2103 [(
2104 "schema".to_string(),
2105 DataSchema {
2106 subtype: Some(DataSchemaSubtype::Null),
2107 ..Default::default()
2108 },
2109 )]
2110 .into_iter()
2111 .collect(),
2112 ),
2113 security_definitions: [("nosec".to_string(), SecurityScheme::default())]
2114 .into_iter()
2115 .collect(),
2116 security: vec!["nosec".to_string()],
2117 profile: Some(vec!["profile1".to_string(), "profile2".to_string()]),
2118 uri_variables: Some(
2119 [
2120 (
2121 "uriVariable1".to_string(),
2122 DataSchema {
2123 subtype: Some(DataSchemaSubtype::String(Default::default())),
2124 ..Default::default()
2125 },
2126 ),
2127 (
2128 "uriVariable2".to_string(),
2129 DataSchema {
2130 subtype: Some(DataSchemaSubtype::Number(Default::default())),
2131 ..Default::default()
2132 },
2133 ),
2134 ]
2135 .into_iter()
2136 .collect(),
2137 ),
2138 ..Default::default()
2139 };
2140
2141 let thing: Thing = serde_json::from_str(RAW).unwrap();
2142 assert_eq!(thing, expected_thing);
2143
2144 let thing: Thing = serde_json::from_value(serde_json::to_value(thing).unwrap()).unwrap();
2145 assert_eq!(thing, expected_thing);
2146 }
2147
2148 #[test]
2149 fn default_context() {
2150 const RAW: &str = r#"
2151 {
2152 "title": "MyLampThing",
2153 "securityDefinitions": {
2154 "nosec": {
2155 "scheme": "nosec"
2156 }
2157 },
2158 "security": [
2159 "nosec"
2160 ]
2161 }"#;
2162
2163 let expected_thing = Thing {
2164 context: TD_CONTEXT_11.into(),
2165 title: "MyLampThing".to_string(),
2166 security_definitions: [("nosec".to_string(), SecurityScheme::default())]
2167 .into_iter()
2168 .collect(),
2169 security: vec!["nosec".to_string()],
2170 ..Default::default()
2171 };
2172
2173 let thing: Thing = serde_json::from_str(RAW).unwrap();
2174 assert_eq!(thing, expected_thing);
2175 }
2176
2177 #[derive(Serialize, Deserialize)]
2178 struct A(i32);
2179
2180 impl Default for A {
2181 fn default() -> Self {
2182 A(42)
2183 }
2184 }
2185
2186 #[derive(Default, Serialize, Deserialize)]
2187 struct ThingExtA {
2188 a: A,
2189 }
2190
2191 #[derive(Default, Serialize, Deserialize)]
2192 struct IntAffExtA {
2193 b: A,
2194 }
2195
2196 #[derive(Default, Serialize, Deserialize)]
2197 struct ActionAffExtA {
2198 c: A,
2199 }
2200
2201 #[derive(Default, Serialize, Deserialize)]
2202 struct PropAffExtA {
2203 d: A,
2204 }
2205
2206 #[derive(Default, Serialize, Deserialize)]
2207 struct EventAffExtA {
2208 e: A,
2209 }
2210
2211 #[derive(Default, Serialize, Deserialize)]
2212 struct FormExtA {
2213 f: A,
2214 }
2215
2216 #[derive(Default, Serialize, Deserialize)]
2217 struct RespExtA {
2218 g: A,
2219 }
2220
2221 #[derive(Default, Serialize, Deserialize)]
2222 struct DataSchemaExtA {
2223 h: A,
2224 }
2225
2226 #[derive(Default, Serialize, Deserialize)]
2227 struct ObjectSchemaExtA {
2228 i: A,
2229 }
2230
2231 #[derive(Default, Serialize, Deserialize)]
2232 struct ArraySchemaExtA {
2233 j: A,
2234 }
2235
2236 impl ExtendableThing for ThingExtA {
2237 type InteractionAffordance = IntAffExtA;
2238 type PropertyAffordance = PropAffExtA;
2239 type ActionAffordance = ActionAffExtA;
2240 type EventAffordance = EventAffExtA;
2241 type Form = FormExtA;
2242 type ExpectedResponse = RespExtA;
2243 type DataSchema = DataSchemaExtA;
2244 type ObjectSchema = ObjectSchemaExtA;
2245 type ArraySchema = ArraySchemaExtA;
2246 }
2247
2248 #[test]
2249 fn extend_single_thing() {
2250 let thing = Thing::<ThingExtA> {
2251 context: "test".into(),
2252 properties: Some(
2253 [(
2254 "prop".to_string(),
2255 PropertyAffordance {
2256 interaction: InteractionAffordance {
2257 other: IntAffExtA { b: A(1) },
2258 ..Default::default()
2259 },
2260 data_schema: DataSchema {
2261 subtype: Some(DataSchemaSubtype::Array(ArraySchema {
2262 other: ArraySchemaExtA { j: A(2) },
2263 ..Default::default()
2264 })),
2265 other: DataSchemaExtA { h: A(3) },
2266 ..Default::default()
2267 },
2268 other: PropAffExtA { d: A(4) },
2269 ..Default::default()
2270 },
2271 )]
2272 .into_iter()
2273 .collect(),
2274 ),
2275 actions: Some(
2276 [(
2277 "action".to_string(),
2278 ActionAffordance {
2279 interaction: InteractionAffordance {
2280 other: IntAffExtA { b: A(5) },
2281 ..Default::default()
2282 },
2283 input: Some(DataSchema {
2284 subtype: Some(DataSchemaSubtype::Object(ObjectSchema {
2285 other: ObjectSchemaExtA { i: A(6) },
2286 ..Default::default()
2287 })),
2288 other: DataSchemaExtA { h: A(7) },
2289 ..Default::default()
2290 }),
2291 output: Some(DataSchema::default()),
2292 other: ActionAffExtA { c: A(8) },
2293 ..Default::default()
2294 },
2295 )]
2296 .into_iter()
2297 .collect(),
2298 ),
2299 events: Some(
2300 [(
2301 "event".to_string(),
2302 EventAffordance {
2303 other: EventAffExtA { e: A(9) },
2304 ..Default::default()
2305 },
2306 )]
2307 .into_iter()
2308 .collect(),
2309 ),
2310 forms: Some(vec![Form {
2311 response: Some(ExpectedResponse {
2312 other: RespExtA { g: A(10) },
2313 ..Default::default()
2314 }),
2315 other: FormExtA { f: A(11) },
2316 ..Default::default()
2317 }]),
2318 schema_definitions: Some(
2319 [(
2320 "schema".to_string(),
2321 DataSchema {
2322 subtype: Some(DataSchemaSubtype::Null),
2323 other: DataSchemaExtA { h: A(12) },
2324 ..Default::default()
2325 },
2326 )]
2327 .into_iter()
2328 .collect(),
2329 ),
2330 other: ThingExtA { a: A(13) },
2331 ..Default::default()
2332 };
2333
2334 let thing_json = serde_json::to_value(thing).unwrap();
2335 assert_eq!(
2336 thing_json,
2337 json![{
2338 "@context": "test",
2339 "title": "",
2340 "properties": {
2341 "prop": {
2342 "b": 1,
2343 "j": 2,
2344 "h": 3,
2345 "d": 4,
2346 "forms": [],
2347 "type": "array",
2348 "readOnly": false,
2349 "writeOnly": false,
2350 }
2351 },
2352 "actions": {
2353 "action": {
2354 "b": 5,
2355 "input": {
2356 "i": 6,
2357 "h": 7,
2358 "readOnly": false,
2359 "writeOnly": false,
2360 "type": "object",
2361 },
2362 "output": {
2363 "h": 42,
2364 "readOnly": false,
2365 "writeOnly": false,
2366 },
2367 "forms": [],
2368 "idempotent": false,
2369 "safe": false,
2370 "c": 8,
2371 }
2372 },
2373 "events": {
2374 "event": {
2375 "b": 42,
2376 "e": 9,
2377 "forms": [],
2378 }
2379 },
2380 "forms": [{
2381 "href": "",
2382 "response": {
2383 "contentType": "",
2384 "g": 10,
2385 },
2386 "f": 11,
2387 }],
2388 "schemaDefinitions": {
2389 "schema": {
2390 "type": "null",
2391 "readOnly": false,
2392 "writeOnly": false,
2393 "h": 12,
2394 }
2395 },
2396 "security": [],
2397 "securityDefinitions": {},
2398 "a": 13,
2399 }],
2400 );
2401 }
2402
2403 #[test]
2404 fn extend_single_thing_with_hlist() {
2405 let thing = Thing::<Cons<ThingExtA, Nil>> {
2406 context: "test".into(),
2407 properties: Some(
2408 [(
2409 "prop".to_string(),
2410 PropertyAffordance {
2411 interaction: InteractionAffordance {
2412 other: Nil::cons(IntAffExtA { b: A(1) }),
2413 ..Default::default()
2414 },
2415 data_schema: DataSchema {
2416 subtype: Some(DataSchemaSubtype::Array(ArraySchema {
2417 other: Nil::cons(ArraySchemaExtA { j: A(2) }),
2418 ..Default::default()
2419 })),
2420 other: Nil::cons(DataSchemaExtA { h: A(3) }),
2421 ..Default::default()
2422 },
2423 other: Nil::cons(PropAffExtA { d: A(4) }),
2424 ..Default::default()
2425 },
2426 )]
2427 .into_iter()
2428 .collect(),
2429 ),
2430 actions: Some(
2431 [(
2432 "action".to_string(),
2433 ActionAffordance {
2434 interaction: InteractionAffordance {
2435 other: Nil::cons(IntAffExtA { b: A(5) }),
2436 ..Default::default()
2437 },
2438 input: Some(DataSchema {
2439 subtype: Some(DataSchemaSubtype::Object(ObjectSchema {
2440 other: Nil::cons(ObjectSchemaExtA { i: A(6) }),
2441 ..Default::default()
2442 })),
2443 other: Nil::cons(DataSchemaExtA { h: A(7) }),
2444 ..Default::default()
2445 }),
2446 output: Some(DataSchema::default()),
2447 other: Nil::cons(ActionAffExtA { c: A(8) }),
2448 ..Default::default()
2449 },
2450 )]
2451 .into_iter()
2452 .collect(),
2453 ),
2454 events: Some(
2455 [(
2456 "event".to_string(),
2457 EventAffordance {
2458 other: Nil::cons(EventAffExtA { e: A(9) }),
2459 ..Default::default()
2460 },
2461 )]
2462 .into_iter()
2463 .collect(),
2464 ),
2465 forms: Some(vec![Form {
2466 response: Some(ExpectedResponse {
2467 other: Nil::cons(RespExtA { g: A(10) }),
2468 ..Default::default()
2469 }),
2470 other: Nil::cons(FormExtA { f: A(11) }),
2471 ..Default::default()
2472 }]),
2473 other: Nil::cons(ThingExtA { a: A(12) }),
2474 ..Default::default()
2475 };
2476
2477 let thing_json = serde_json::to_value(thing).unwrap();
2478 assert_eq!(
2479 thing_json,
2480 json!({
2481 "@context": "test",
2482 "title": "",
2483 "properties": {
2484 "prop": {
2485 "b": 1,
2486 "j": 2,
2487 "h": 3,
2488 "d": 4,
2489 "forms": [],
2490 "type": "array",
2491 "readOnly": false,
2492 "writeOnly": false,
2493 }
2494 },
2495 "actions": {
2496 "action": {
2497 "b": 5,
2498 "input": {
2499 "i": 6,
2500 "h": 7,
2501 "readOnly": false,
2502 "writeOnly": false,
2503 "type": "object",
2504 },
2505 "output": {
2506 "h": 42,
2507 "readOnly": false,
2508 "writeOnly": false,
2509 },
2510 "forms": [],
2511 "idempotent": false,
2512 "safe": false,
2513 "c": 8,
2514 }
2515 },
2516 "events": {
2517 "event": {
2518 "b": 42,
2519 "e": 9,
2520 "forms": [],
2521 }
2522 },
2523 "forms": [{
2524 "href": "",
2525 "response": {
2526 "contentType": "",
2527 "g": 10,
2528 },
2529 "f": 11,
2530 }],
2531 "security": [],
2532 "securityDefinitions": {},
2533 "a": 12,
2534 }),
2535 );
2536 }
2537
2538 #[derive(Default, Serialize, Deserialize)]
2539 struct ThingExtB {
2540 k: A,
2541 }
2542
2543 #[derive(Default, Serialize, Deserialize)]
2544 struct IntAffExtB {
2545 l: A,
2546 }
2547
2548 #[derive(Default, Serialize, Deserialize)]
2549 struct ActionAffExtB {
2550 m: A,
2551 }
2552
2553 #[derive(Default, Serialize, Deserialize)]
2554 struct PropAffExtB {
2555 n: A,
2556 }
2557
2558 #[derive(Default, Serialize, Deserialize)]
2559 struct EventAffExtB {
2560 o: A,
2561 }
2562
2563 #[derive(Default, Serialize, Deserialize)]
2564 struct FormExtB {
2565 p: A,
2566 }
2567
2568 #[derive(Default, Serialize, Deserialize)]
2569 struct RespExtB {
2570 q: A,
2571 }
2572
2573 #[derive(Default, Serialize, Deserialize)]
2574 struct DataSchemaExtB {
2575 r: A,
2576 }
2577
2578 #[derive(Default, Serialize, Deserialize)]
2579 struct ObjectSchemaExtB {
2580 s: A,
2581 }
2582
2583 #[derive(Default, Serialize, Deserialize)]
2584 struct ArraySchemaExtB {
2585 t: A,
2586 }
2587
2588 impl ExtendableThing for ThingExtB {
2589 type InteractionAffordance = IntAffExtB;
2590 type PropertyAffordance = PropAffExtB;
2591 type ActionAffordance = ActionAffExtB;
2592 type EventAffordance = EventAffExtB;
2593 type Form = FormExtB;
2594 type ExpectedResponse = RespExtB;
2595 type DataSchema = DataSchemaExtB;
2596 type ObjectSchema = ObjectSchemaExtB;
2597 type ArraySchema = ArraySchemaExtB;
2598 }
2599
2600 #[test]
2601 fn extend_thing_with_two() {
2602 let thing = Thing::<Cons<ThingExtB, Cons<ThingExtA, Nil>>> {
2603 context: "test".into(),
2604 properties: Some(
2605 [(
2606 "prop".to_string(),
2607 PropertyAffordance {
2608 interaction: InteractionAffordance {
2609 other: Nil::cons(IntAffExtA { b: A(1) }).cons(IntAffExtB { l: A(2) }),
2610 ..Default::default()
2611 },
2612 data_schema: DataSchema {
2613 subtype: Some(DataSchemaSubtype::Array(ArraySchema {
2614 other: Nil::cons(ArraySchemaExtA { j: A(3) })
2615 .cons(ArraySchemaExtB { t: A(4) }),
2616 ..Default::default()
2617 })),
2618 other: Nil::cons(DataSchemaExtA { h: A(5) })
2619 .cons(DataSchemaExtB { r: A(6) }),
2620 ..Default::default()
2621 },
2622 other: Nil::cons(PropAffExtA { d: A(7) }).cons(PropAffExtB { n: A(8) }),
2623 ..Default::default()
2624 },
2625 )]
2626 .into_iter()
2627 .collect(),
2628 ),
2629 actions: Some(
2630 [(
2631 "action".to_string(),
2632 ActionAffordance {
2633 interaction: InteractionAffordance {
2634 other: Nil::cons(IntAffExtA { b: A(9) }).cons(IntAffExtB { l: A(10) }),
2635 ..Default::default()
2636 },
2637 input: Some(DataSchema {
2638 subtype: Some(DataSchemaSubtype::Object(ObjectSchema {
2639 other: Nil::cons(ObjectSchemaExtA { i: A(11) })
2640 .cons(ObjectSchemaExtB { s: A(12) }),
2641 ..Default::default()
2642 })),
2643 other: Nil::cons(DataSchemaExtA { h: A(13) })
2644 .cons(DataSchemaExtB { r: A(14) }),
2645 ..Default::default()
2646 }),
2647 output: Some(DataSchema::default()),
2648 other: Nil::cons(ActionAffExtA { c: A(15) })
2649 .cons(ActionAffExtB { m: A(16) }),
2650 ..Default::default()
2651 },
2652 )]
2653 .into_iter()
2654 .collect(),
2655 ),
2656 events: Some(
2657 [(
2658 "event".to_string(),
2659 EventAffordance {
2660 other: Nil::cons(EventAffExtA { e: A(17) }).cons(EventAffExtB { o: A(18) }),
2661 ..Default::default()
2662 },
2663 )]
2664 .into_iter()
2665 .collect(),
2666 ),
2667 forms: Some(vec![Form {
2668 response: Some(ExpectedResponse {
2669 other: Nil::cons(RespExtA { g: A(19) }).cons(RespExtB { q: A(20) }),
2670 ..Default::default()
2671 }),
2672 other: Nil::cons(FormExtA { f: A(21) }).cons(FormExtB { p: A(22) }),
2673 ..Default::default()
2674 }]),
2675 other: Nil::cons(ThingExtA { a: A(23) }).cons(ThingExtB { k: A(24) }),
2676 ..Default::default()
2677 };
2678
2679 let thing_json = serde_json::to_value(thing).unwrap();
2680 assert_eq!(
2681 thing_json,
2682 json!({
2683 "@context": "test",
2684 "title": "",
2685 "properties": {
2686 "prop": {
2687 "b": 1,
2688 "l": 2,
2689 "j": 3,
2690 "t": 4,
2691 "h": 5,
2692 "r": 6,
2693 "d": 7,
2694 "n": 8,
2695 "forms": [],
2696 "type": "array",
2697 "readOnly": false,
2698 "writeOnly": false,
2699 }
2700 },
2701 "actions": {
2702 "action": {
2703 "b": 9,
2704 "l": 10,
2705 "input": {
2706 "i": 11,
2707 "s": 12,
2708 "h": 13,
2709 "r": 14,
2710 "readOnly": false,
2711 "writeOnly": false,
2712 "type": "object",
2713 },
2714 "output": {
2715 "h": 42,
2716 "r": 42,
2717 "readOnly": false,
2718 "writeOnly": false,
2719 },
2720 "forms": [],
2721 "idempotent": false,
2722 "safe": false,
2723 "c": 15,
2724 "m": 16,
2725 }
2726 },
2727 "events": {
2728 "event": {
2729 "b": 42,
2730 "l": 42,
2731 "e": 17,
2732 "o": 18,
2733 "forms": [],
2734 }
2735 },
2736 "forms": [{
2737 "href": "",
2738 "response": {
2739 "contentType": "",
2740 "g": 19,
2741 "q": 20,
2742 },
2743 "f": 21,
2744 "p": 22,
2745 }],
2746 "security": [],
2747 "securityDefinitions": {},
2748 "a": 23,
2749 "k": 24,
2750 }),
2751 );
2752 }
2753
2754 #[test]
2755 fn dummy_http() {
2756 #[derive(Serialize, Deserialize, Default)]
2757 struct HttpThing {}
2758
2759 #[derive(Deserialize, Serialize)]
2760 #[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2761 enum HttpMethod {
2762 Get,
2763 Put,
2764 Post,
2765 Delete,
2766 Patch,
2767 }
2768
2769 #[derive(Deserialize, Serialize)]
2770 struct HttpMessageHeader {
2771 #[serde(rename = "htv:fieldName")]
2772 field_name: Option<String>,
2773 #[serde(rename = "htv:fieldValue")]
2774 field_value: Option<String>,
2775 }
2776
2777 #[derive(Deserialize, Serialize, Default)]
2778 struct HttpResponse {
2779 #[serde(rename = "htv:headers")]
2780 headers: Vec<HttpMessageHeader>,
2781 #[serde(rename = "htv:statusCodeValue")]
2782 status_code_value: Option<usize>,
2783 }
2784
2785 #[derive(Default, Deserialize, Serialize)]
2786 struct HttpForm {
2787 #[serde(rename = "htv:methodName")]
2788 method_name: Option<HttpMethod>,
2789 }
2790
2791 impl ExtendableThing for HttpThing {
2792 type InteractionAffordance = ();
2793 type PropertyAffordance = ();
2794 type ActionAffordance = ();
2795 type EventAffordance = ();
2796 type Form = HttpForm;
2797 type ExpectedResponse = HttpResponse;
2798 type DataSchema = ();
2799 type ObjectSchema = ();
2800 type ArraySchema = ();
2801 }
2802
2803 let thing = Thing::<Cons<ThingExtB, Cons<HttpThing, Cons<ThingExtA, Nil>>>> {
2804 context: "test".into(),
2805 properties: Some(
2806 [(
2807 "prop".to_string(),
2808 PropertyAffordance {
2809 interaction: InteractionAffordance {
2810 other: Nil::cons(IntAffExtA { b: A(1) })
2811 .cons(())
2812 .cons(IntAffExtB { l: A(2) }),
2813 ..Default::default()
2814 },
2815 data_schema: DataSchema {
2816 subtype: Some(DataSchemaSubtype::Array(ArraySchema {
2817 other: Nil::cons(ArraySchemaExtA { j: A(3) })
2818 .cons(())
2819 .cons(ArraySchemaExtB { t: A(4) }),
2820 ..Default::default()
2821 })),
2822 other: Nil::cons(DataSchemaExtA { h: A(5) })
2823 .cons(())
2824 .cons(DataSchemaExtB { r: A(6) }),
2825 ..Default::default()
2826 },
2827 other: Nil::cons(PropAffExtA { d: A(7) })
2828 .cons(())
2829 .cons(PropAffExtB { n: A(8) }),
2830 ..Default::default()
2831 },
2832 )]
2833 .into_iter()
2834 .collect(),
2835 ),
2836 actions: Some(
2837 [(
2838 "action".to_string(),
2839 ActionAffordance {
2840 interaction: InteractionAffordance {
2841 forms: vec![Form {
2842 other: Nil::cons(FormExtA::default())
2843 .cons(HttpForm {
2844 method_name: Some(HttpMethod::Put),
2845 })
2846 .cons(FormExtB::default()),
2847 ..Default::default()
2848 }],
2849 other: Nil::cons(IntAffExtA { b: A(9) })
2850 .cons(())
2851 .cons(IntAffExtB { l: A(10) }),
2852 ..Default::default()
2853 },
2854 input: Some(DataSchema {
2855 subtype: Some(DataSchemaSubtype::Object(ObjectSchema {
2856 other: Nil::cons(ObjectSchemaExtA { i: A(11) })
2857 .cons(())
2858 .cons(ObjectSchemaExtB { s: A(12) }),
2859 ..Default::default()
2860 })),
2861 other: Nil::cons(DataSchemaExtA { h: A(13) })
2862 .cons(())
2863 .cons(DataSchemaExtB { r: A(14) }),
2864 ..Default::default()
2865 }),
2866 output: Some(DataSchema::default()),
2867 other: Nil::cons(ActionAffExtA { c: A(15) })
2868 .cons(())
2869 .cons(ActionAffExtB { m: A(16) }),
2870 ..Default::default()
2871 },
2872 )]
2873 .into_iter()
2874 .collect(),
2875 ),
2876 events: Some(
2877 [(
2878 "event".to_string(),
2879 EventAffordance {
2880 other: Nil::cons(EventAffExtA { e: A(17) })
2881 .cons(())
2882 .cons(EventAffExtB { o: A(18) }),
2883 ..Default::default()
2884 },
2885 )]
2886 .into_iter()
2887 .collect(),
2888 ),
2889 forms: Some(vec![Form {
2890 response: Some(ExpectedResponse {
2891 other: Nil::cons(RespExtA { g: A(19) })
2892 .cons(HttpResponse {
2893 headers: vec![HttpMessageHeader {
2894 field_name: Some("hello".to_string()),
2895 field_value: Some("world".to_string()),
2896 }],
2897 status_code_value: Some(200),
2898 })
2899 .cons(RespExtB { q: A(20) }),
2900 ..Default::default()
2901 }),
2902 other: Nil::cons(FormExtA { f: A(21) })
2903 .cons(HttpForm {
2904 method_name: Some(HttpMethod::Get),
2905 })
2906 .cons(FormExtB { p: A(22) }),
2907 ..Default::default()
2908 }]),
2909 other: Nil::cons(ThingExtA { a: A(23) })
2910 .cons(HttpThing {})
2911 .cons(ThingExtB { k: A(24) }),
2912 ..Default::default()
2913 };
2914
2915 let thing_json = serde_json::to_value(thing).unwrap();
2916 assert_eq!(
2917 thing_json,
2918 json!({
2919 "@context": "test",
2920 "title": "",
2921 "properties": {
2922 "prop": {
2923 "b": 1,
2924 "l": 2,
2925 "j": 3,
2926 "t": 4,
2927 "h": 5,
2928 "r": 6,
2929 "d": 7,
2930 "n": 8,
2931 "forms": [],
2932 "type": "array",
2933 "readOnly": false,
2934 "writeOnly": false,
2935 }
2936 },
2937 "actions": {
2938 "action": {
2939 "b": 9,
2940 "l": 10,
2941 "input": {
2942 "i": 11,
2943 "s": 12,
2944 "h": 13,
2945 "r": 14,
2946 "readOnly": false,
2947 "writeOnly": false,
2948 "type": "object",
2949 },
2950 "output": {
2951 "h": 42,
2952 "r": 42,
2953 "readOnly": false,
2954 "writeOnly": false,
2955 },
2956 "forms": [
2957 {
2958 "f": 42,
2959 "href": "",
2960 "htv:methodName": "PUT",
2961 "p": 42,
2962 }
2963 ],
2964 "idempotent": false,
2965 "safe": false,
2966 "c": 15,
2967 "m": 16,
2968 }
2969 },
2970 "events": {
2971 "event": {
2972 "b": 42,
2973 "l": 42,
2974 "e": 17,
2975 "o": 18,
2976 "forms": [],
2977 }
2978 },
2979 "forms": [{
2980 "href": "",
2981 "response": {
2982 "contentType": "",
2983 "g": 19,
2984 "q": 20,
2985 "htv:headers": [{
2986 "htv:fieldName": "hello",
2987 "htv:fieldValue": "world",
2988 }],
2989 "htv:statusCodeValue": 200,
2990 },
2991 "f": 21,
2992 "p": 22,
2993 "htv:methodName": "GET",
2994 }],
2995 "security": [],
2996 "securityDefinitions": {},
2997 "a": 23,
2998 "k": 24,
2999 }),
3000 );
3001 }
3002
3003 #[derive(Debug, PartialEq, Serialize, Deserialize)]
3004 struct DataSchemaExt {}
3005
3006 #[derive(Debug, PartialEq, Serialize, Deserialize)]
3007 struct ArraySchemaExt {}
3008
3009 #[derive(Debug, PartialEq, Serialize, Deserialize)]
3010 struct ObjectSchemaExt {}
3011
3012 #[test]
3013 fn default_array_schema() {
3014 ArraySchema::<DataSchemaExt, (), ObjectSchemaExt>::default();
3015 }
3016
3017 #[test]
3018 fn default_object_schema() {
3019 ObjectSchema::<DataSchemaExt, ArraySchemaExt, ()>::default();
3020 }
3021
3022 #[test]
3023 fn serde_empty_additional_expected_response() {
3024 let response: AdditionalExpectedResponse = serde_json::from_value(json!({})).unwrap();
3025 assert_eq!(
3026 response,
3027 AdditionalExpectedResponse {
3028 success: false,
3029 content_type: None,
3030 schema: None,
3031 },
3032 );
3033
3034 assert_eq!(serde_json::to_value(response).unwrap(), json!({}));
3035 }
3036
3037 #[test]
3038 fn serde_full_additional_expected_response() {
3039 let raw_data = json!({
3040 "success": true,
3041 "contentType": "application/json",
3042 "schema": "test",
3043 });
3044
3045 let response: AdditionalExpectedResponse =
3046 serde_json::from_value(raw_data.clone()).unwrap();
3047
3048 assert_eq!(
3049 response,
3050 AdditionalExpectedResponse {
3051 success: true,
3052 content_type: Some("application/json".to_string()),
3053 schema: Some("test".to_string()),
3054 },
3055 );
3056
3057 assert_eq!(serde_json::to_value(response).unwrap(), raw_data);
3058 }
3059
3060 #[test]
3061 fn combo_security_scheme() {
3062 let raw_data = json!({
3063 "oneOf": "simple",
3064 });
3065 let combo: ComboSecurityScheme = serde_json::from_value(raw_data.clone()).unwrap();
3066 assert_eq!(
3067 combo,
3068 ComboSecurityScheme::OneOf(vec!["simple".to_string()]),
3069 );
3070 assert_eq!(serde_json::to_value(combo).unwrap(), raw_data);
3071
3072 let raw_data = json!({
3073 "oneOf": ["data1", "data2"],
3074 });
3075 let combo: ComboSecurityScheme = serde_json::from_value(raw_data.clone()).unwrap();
3076 assert_eq!(
3077 combo,
3078 ComboSecurityScheme::OneOf(vec!["data1".to_string(), "data2".to_string()]),
3079 );
3080 assert_eq!(serde_json::to_value(combo).unwrap(), raw_data);
3081
3082 let raw_data = json!({
3083 "allOf": "simple",
3084 });
3085 let combo: ComboSecurityScheme = serde_json::from_value(raw_data.clone()).unwrap();
3086 assert_eq!(
3087 combo,
3088 ComboSecurityScheme::AllOf(vec!["simple".to_string()]),
3089 );
3090 assert_eq!(serde_json::to_value(combo).unwrap(), raw_data);
3091
3092 let raw_data = json!({
3093 "allOf": ["data1", "data2"],
3094 });
3095 let combo: ComboSecurityScheme = serde_json::from_value(raw_data.clone()).unwrap();
3096 assert_eq!(
3097 combo,
3098 ComboSecurityScheme::AllOf(vec!["data1".to_string(), "data2".to_string()]),
3099 );
3100 assert_eq!(serde_json::to_value(combo).unwrap(), raw_data);
3101 }
3102
3103 #[test]
3104 fn minimum_partial_ord_trivial() {
3105 assert_eq!(
3106 Minimum::Inclusive(5).partial_cmp(&Minimum::Inclusive(5)),
3107 Some(Ordering::Equal),
3108 );
3109 assert_eq!(
3110 Minimum::Inclusive(5).partial_cmp(&Minimum::Inclusive(6)),
3111 Some(Ordering::Less),
3112 );
3113 assert_eq!(
3114 Minimum::Inclusive(6).partial_cmp(&Minimum::Inclusive(5)),
3115 Some(Ordering::Greater),
3116 );
3117
3118 assert_eq!(
3119 Minimum::Exclusive(5).partial_cmp(&Minimum::Exclusive(5)),
3120 Some(Ordering::Equal),
3121 );
3122 assert_eq!(
3123 Minimum::Exclusive(5).partial_cmp(&Minimum::Exclusive(6)),
3124 Some(Ordering::Less),
3125 );
3126 assert_eq!(
3127 Minimum::Exclusive(6).partial_cmp(&Minimum::Exclusive(5)),
3128 Some(Ordering::Greater),
3129 );
3130 }
3131
3132 #[test]
3133 fn minimum_partial_ord_complex() {
3134 assert_eq!(
3135 Minimum::Inclusive(4).partial_cmp(&Minimum::Exclusive(5)),
3136 Some(Ordering::Less),
3137 );
3138
3139 assert_eq!(
3140 Minimum::Inclusive(5).partial_cmp(&Minimum::Exclusive(5)),
3141 Some(Ordering::Less),
3142 );
3143
3144 assert_eq!(
3145 Minimum::Inclusive(6).partial_cmp(&Minimum::Exclusive(5)),
3146 None,
3147 );
3148
3149 assert_eq!(
3150 Minimum::Exclusive(4).partial_cmp(&Minimum::Inclusive(5)),
3151 None,
3152 );
3153
3154 assert_eq!(
3155 Minimum::Exclusive(5).partial_cmp(&Minimum::Inclusive(5)),
3156 Some(Ordering::Greater),
3157 );
3158
3159 assert_eq!(
3160 Minimum::Exclusive(6).partial_cmp(&Minimum::Inclusive(5)),
3161 Some(Ordering::Greater),
3162 );
3163 }
3164
3165 #[test]
3166 fn maximum_partial_ord_trivial() {
3167 use core::cmp::Ordering;
3168 assert_eq!(
3169 Maximum::Inclusive(5).partial_cmp(&Maximum::Inclusive(5)),
3170 Some(Ordering::Equal),
3171 );
3172 assert_eq!(
3173 Maximum::Inclusive(5).partial_cmp(&Maximum::Inclusive(6)),
3174 Some(Ordering::Less),
3175 );
3176 assert_eq!(
3177 Maximum::Inclusive(6).partial_cmp(&Maximum::Inclusive(5)),
3178 Some(Ordering::Greater),
3179 );
3180
3181 assert_eq!(
3182 Maximum::Exclusive(5).partial_cmp(&Maximum::Exclusive(5)),
3183 Some(Ordering::Equal),
3184 );
3185 assert_eq!(
3186 Maximum::Exclusive(5).partial_cmp(&Maximum::Exclusive(6)),
3187 Some(Ordering::Less),
3188 );
3189 assert_eq!(
3190 Maximum::Exclusive(6).partial_cmp(&Maximum::Exclusive(5)),
3191 Some(Ordering::Greater),
3192 );
3193 }
3194
3195 #[test]
3196 fn maximum_partial_ord_complex() {
3197 assert_eq!(
3198 Maximum::Inclusive(4).partial_cmp(&Maximum::Exclusive(5)),
3199 None,
3200 );
3201
3202 assert_eq!(
3203 Maximum::Inclusive(5).partial_cmp(&Maximum::Exclusive(5)),
3204 Some(Ordering::Greater),
3205 );
3206
3207 assert_eq!(
3208 Maximum::Inclusive(6).partial_cmp(&Maximum::Exclusive(5)),
3209 Some(Ordering::Greater),
3210 );
3211
3212 assert_eq!(
3213 Maximum::Exclusive(4).partial_cmp(&Maximum::Inclusive(5)),
3214 Some(Ordering::Less)
3215 );
3216
3217 assert_eq!(
3218 Maximum::Exclusive(5).partial_cmp(&Maximum::Inclusive(5)),
3219 Some(Ordering::Less),
3220 );
3221
3222 assert_eq!(
3223 Maximum::Exclusive(6).partial_cmp(&Maximum::Inclusive(5)),
3224 None
3225 );
3226 }
3227
3228 #[test]
3229 fn minimum_maximum_mixed_partial_ord_trivial() {
3230 assert_eq!(
3231 Minimum::Inclusive(4).partial_cmp(&Maximum::Inclusive(5)),
3232 Some(Ordering::Less),
3233 );
3234 assert_eq!(
3235 Minimum::Inclusive(5).partial_cmp(&Maximum::Inclusive(5)),
3236 Some(Ordering::Equal),
3237 );
3238 assert_eq!(
3239 Minimum::Inclusive(6).partial_cmp(&Maximum::Inclusive(5)),
3240 Some(Ordering::Greater),
3241 );
3242
3243 assert_eq!(
3244 Maximum::Inclusive(4).partial_cmp(&Minimum::Inclusive(5)),
3245 Some(Ordering::Less),
3246 );
3247 assert_eq!(
3248 Maximum::Inclusive(5).partial_cmp(&Minimum::Inclusive(5)),
3249 Some(Ordering::Equal),
3250 );
3251 assert_eq!(
3252 Maximum::Inclusive(6).partial_cmp(&Minimum::Inclusive(5)),
3253 Some(Ordering::Greater),
3254 );
3255 }
3256
3257 #[test]
3258 fn minimum_maximum_mixed_partial_ord_complex() {
3259 assert_eq!(
3260 Minimum::Inclusive(4).partial_cmp(&Maximum::Exclusive(5)),
3261 None,
3262 );
3263 assert_eq!(
3264 Minimum::Inclusive(5).partial_cmp(&Maximum::Exclusive(5)),
3265 Some(Ordering::Greater)
3266 );
3267 assert_eq!(
3268 Minimum::Inclusive(6).partial_cmp(&Maximum::Exclusive(5)),
3269 Some(Ordering::Greater)
3270 );
3271
3272 assert_eq!(
3273 Minimum::Exclusive(4).partial_cmp(&Maximum::Inclusive(5)),
3274 None,
3275 );
3276 assert_eq!(
3277 Minimum::Exclusive(5).partial_cmp(&Maximum::Inclusive(5)),
3278 Some(Ordering::Greater),
3279 );
3280 assert_eq!(
3281 Minimum::Exclusive(6).partial_cmp(&Maximum::Inclusive(5)),
3282 Some(Ordering::Greater),
3283 );
3284
3285 assert_eq!(
3286 Maximum::Inclusive(4).partial_cmp(&Minimum::Exclusive(5)),
3287 Some(Ordering::Less),
3288 );
3289 assert_eq!(
3290 Maximum::Inclusive(5).partial_cmp(&Minimum::Exclusive(5)),
3291 Some(Ordering::Less),
3292 );
3293 assert_eq!(
3294 Maximum::Inclusive(6).partial_cmp(&Minimum::Exclusive(5)),
3295 None,
3296 );
3297
3298 assert_eq!(
3299 Maximum::Exclusive(4).partial_cmp(&Minimum::Inclusive(5)),
3300 Some(Ordering::Less),
3301 );
3302 assert_eq!(
3303 Maximum::Exclusive(5).partial_cmp(&Minimum::Inclusive(5)),
3304 Some(Ordering::Less),
3305 );
3306 assert_eq!(
3307 Maximum::Exclusive(6).partial_cmp(&Minimum::Inclusive(5)),
3308 None,
3309 );
3310 }
3311
3312 #[test]
3313 fn serde_number_schema() {
3314 let data: NumberSchema = serde_json::from_value(json! {
3315 {
3316 "minimum": 0.5,
3317 "maximum": 1.,
3318 "multipleOf": 0.5,
3319 }
3320 })
3321 .unwrap();
3322
3323 assert_eq!(
3324 data,
3325 NumberSchema {
3326 minimum: Some(Minimum::Inclusive(0.5)),
3327 maximum: Some(Maximum::Inclusive(1.)),
3328 multiple_of: Some(0.5),
3329 },
3330 );
3331
3332 let data: NumberSchema = serde_json::from_value(json! {
3333 {
3334 "exclusiveMinimum": 0.5,
3335 "exclusiveMaximum": 1.,
3336 "multipleOf": 0.5,
3337 }
3338 })
3339 .unwrap();
3340
3341 assert_eq!(
3342 data,
3343 NumberSchema {
3344 minimum: Some(Minimum::Exclusive(0.5)),
3345 maximum: Some(Maximum::Exclusive(1.)),
3346 multiple_of: Some(0.5),
3347 },
3348 );
3349 }
3350
3351 #[test]
3352 fn serde_integer_schema() {
3353 let data: IntegerSchema = serde_json::from_value(json! {
3354 {
3355 "minimum": 5,
3356 "maximum": 10,
3357 "multipleOf": 2,
3358 }
3359 })
3360 .unwrap();
3361
3362 assert_eq!(
3363 data,
3364 IntegerSchema {
3365 minimum: Some(Minimum::Inclusive(5)),
3366 maximum: Some(Maximum::Inclusive(10)),
3367 multiple_of: Some(NonZeroU64::new(2).unwrap()),
3368 },
3369 );
3370
3371 let data: IntegerSchema = serde_json::from_value(json! {
3372 {
3373 "exclusiveMinimum": 5,
3374 "exclusiveMaximum": 10,
3375 }
3376 })
3377 .unwrap();
3378
3379 assert_eq!(
3380 data,
3381 IntegerSchema {
3382 minimum: Some(Minimum::Exclusive(5)),
3383 maximum: Some(Maximum::Exclusive(10)),
3384 multiple_of: None,
3385 },
3386 );
3387 }
3388
3389 #[test]
3390 fn form_almost_default_serialization() {
3391 let form: Form<Nil> = Form {
3392 href: "href".to_string(),
3393 ..Default::default()
3394 };
3395
3396 let form_json = serde_json::to_value(form).unwrap();
3397 assert_eq!(
3398 form_json,
3399 json!({
3400 "href": "href",
3401 }),
3402 )
3403 }
3404}