wot_td/
thing.rs

1//! Thing Description data structures
2//!
3//! A Thing Description, or `TD`, stores the semantic metadata and the interface descriptions of
4//! a physical or virtual entity, called `Thing`.
5//!
6//! Use [Thing::builder] to build a new `Thing`, [serde_json] to serialize or deserialize it.
7//!
8//! [Interaction Affordance]: https://www.w3.org/TR/wot-thing-description/#interactionaffordance
9
10use 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
40/// The JSON-LD context for the version 1.0 of the [Thing
41/// description](https://www.w3.org/TR/wot-thing-description/)
42pub const TD_CONTEXT_10: &str = "https://www.w3.org/2019/wot/td/v1";
43
44/// The JSON-LD context for the version 1.1 of the [Thing
45/// description](https://www.w3.org/TR/wot-thing-description11/)
46pub 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    // time uses std::io::Write internally making the serializer unavailable
58    // for no_std
59    // https://github.com/time-rs/time/issues/375
60    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/// An abstraction of a physical or a virtual entity
137///
138/// It contains metadata and a description of its interfaces.
139#[serde_as]
140#[skip_serializing_none]
141#[derive(Deserialize, Serialize)]
142#[serde(rename_all = "camelCase")]
143pub struct Thing<Other: ExtendableThing = Nil> {
144    // The context can be arbitrarily complex
145    // https://www.w3.org/TR/json-ld11/#the-context
146    // Let's take a value for now and assume we'll use the json-ld crate later
147    /// A [JSON-LD @context](https://www.w3.org/TR/json-ld11/#the-context)
148    #[serde(rename = "@context", default = "default_context")]
149    pub context: Value,
150
151    /// A unique identifier
152    pub id: Option<String>,
153
154    /// JSON-LD semantic keywords
155    #[serde(rename = "@type", default)]
156    #[serde_as(as = "Option<OneOrMany<_>>")]
157    pub attype: Option<Vec<String>>,
158
159    /// Human-readable title to be displayed
160    pub title: String,
161
162    /// Multi-language translations of the title
163    pub titles: Option<MultiLanguage>,
164
165    /// Human-readable additional information
166    pub description: Option<String>,
167
168    /// Multi-language translations of the description
169    pub descriptions: Option<MultiLanguage>,
170
171    /// Version information
172    pub version: Option<VersionInfo>,
173
174    /// Time of creation of this description
175    ///
176    /// It may be used for caching purposes.
177    #[serde(with = "rfc3339_option", default)]
178    pub created: Option<OffsetDateTime>,
179
180    /// Time of last update of this description
181    ///
182    /// It may be used for caching purposes.
183    #[serde(with = "rfc3339_option", default)]
184    pub modified: Option<OffsetDateTime>,
185
186    /// URI to the device maintainer
187    ///
188    /// To be used to ask for support.
189    // FIXME: use AnyURI
190    pub support: Option<String>,
191
192    /// Base URI to be used to resolve all the other relative URIs
193    ///
194    /// NOTE: the JSON-LD @context is excluded.
195    // FIXME: use AnyURI
196    pub base: Option<String>,
197
198    /// Property-based [Interaction Affordances]
199    pub properties: Option<HashMap<String, PropertyAffordance<Other>>>,
200
201    /// Action-based [Interaction Affordances]
202    pub actions: Option<HashMap<String, ActionAffordance<Other>>>,
203
204    /// Event-based [Interaction Affordances]
205    pub events: Option<HashMap<String, EventAffordance<Other>>>,
206
207    /// Arbitrary resources that relate to the current Thing
208    ///
209    /// Its meaning depends on the @context and the semantic attributes attached.
210    pub links: Option<Vec<Link>>,
211
212    /// Bulk-operations over the Thing properties
213    pub forms: Option<Vec<Form<Other>>>,
214
215    /// Thing-wide Security constraints
216    ///
217    /// It is a list of names matching the Security Schemes defined in [Thing::security_definitions].
218    /// They must be all satisfied in order to access the Thing resources.
219    #[serde_as(as = "OneOrMany<_>")]
220    pub security: Vec<String>,
221
222    /// Security definitions
223    ///
224    /// A Map of Security Schemes, the name keys are used in [Form::security] and [Thing::security]
225    /// to express all the security constraints that must be satisfied in order to access the
226    /// resources.
227    pub security_definitions: HashMap<String, SecurityScheme>,
228
229    /// URI template variables
230    ///
231    /// A Map of URI template variables that can be used inside `Forms`. The Thing level
232    /// `uri_variables` can be used in Thing-level forms or in [`InteractionAffordance`]. The
233    /// individual variables `DataSchema` cannot be an [`ObjectSchema`] or an [`ArraySchema`]. If
234    /// the same variable is both declared in Thing-level `uri_variables` and in
235    /// [`InteractionAffordance`] level, the `InteractionAffordance` level variable takes
236    /// precedence.
237    pub uri_variables: Option<DataSchemaMap<Other>>,
238
239    /// The WoT profile
240    ///
241    /// Indicates the WoT Profile mechanisms followed by this Thing Description and the
242    /// corresponding Thing implementation.
243    #[serde(default)]
244    #[serde_as(as = "Option<OneOrMany<_>>")]
245    pub profile: Option<Vec<String>>,
246
247    /// A Map of named data schemas
248    ///
249    /// To be used in a schema name-value pair inside an [`AdditionalExpectedResponse`] object.
250    pub schema_definitions: Option<DataSchemaMap<Other>>,
251
252    /// Thing extension
253    #[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    /// Shorthand for [ThingBuilder::new].
370    #[inline]
371    pub fn builder(title: impl Into<String>) -> ThingBuilder<Nil, ToExtend> {
372        ThingBuilder::new(title)
373    }
374}
375
376/// Thing description Interaction Affordance
377///
378/// Metadata of a Thing that shows the possible choices to Consumers, thereby suggesting how
379/// Consumers may interact with the Thing. See [w3c
380/// documentation](https://www.w3.org/TR/wot-thing-description11/#interactionaffordance) for
381/// further details.
382#[serde_as]
383#[skip_serializing_none]
384#[derive(Deserialize, Serialize)]
385#[serde(rename_all = "camelCase")]
386pub struct InteractionAffordance<Other: ExtendableThing> {
387    /// JSON-LD keyword to label the object with semantic tags or types.
388    #[serde(rename = "@type", default)]
389    #[serde_as(as = "Option<OneOrMany<_>>")]
390    pub attype: Option<Vec<String>>,
391
392    /// A human-readable title based on a default language.
393    pub title: Option<String>,
394
395    /// Multi-language human-readable titles.
396    pub titles: Option<MultiLanguage>,
397
398    /// Additional human-readable information based on a default language.
399    pub description: Option<String>,
400
401    /// Additional human-readable information in different languages.
402    pub descriptions: Option<MultiLanguage>,
403
404    /// Set of form hypermedia controls that describe how an operation can be performed.
405    pub forms: Vec<Form<Other>>,
406
407    /// URI template variables
408    ///
409    /// A Map of URI template variables that can be used inside `Forms`. The individual variables
410    /// `DataSchema` cannot be an [`ObjectSchema`] or an [`ArraySchema`]. If the same variable is
411    /// both declared in [`Thing`]-level `uri_variables` and in `InteractionAffordance` level, the
412    /// `InteractionAffordance` level variable takes precedence.
413    pub uri_variables: Option<DataSchemaMap<Other>>,
414
415    /// Interaction affordance extension
416    #[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
481// Do not serialize the fields that are shared with DataSchema
482fn 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/// An affordance that exposes the state of a `Thing`
504///
505/// The fields `title`, `titles`, `description`, `descriptions`,
506/// are serialized from `PropertyAffordance::data_schema`.
507#[skip_serializing_none]
508#[derive(Deserialize, Serialize)]
509pub struct PropertyAffordance<Other: ExtendableThing> {
510    /// The interaction affordance.
511    #[serde(flatten)]
512    #[serde(serialize_with = "omit_common")]
513    pub interaction: InteractionAffordance<Other>,
514
515    /// The data schema representing the property.
516    #[serde(flatten)]
517    pub data_schema: DataSchemaFromOther<Other>,
518
519    /// A hint that indicates whether Servients hosting the Thing and Intermediaries should provide
520    /// a Protocol Binding that supports the [`ObserveProperty`] and [`UnobserveProperty`]
521    /// operations for this property.
522    ///
523    /// [`ObserveProperty`]: FormOperation::ObserveProperty
524    /// [`UnobserveProperty`]: FormOperation::UnobserveProperty
525    pub observable: Option<bool>,
526
527    /// Property affordance extension.
528    #[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/// An affordance that allows to inkvoke a function of the `Thing`.
582#[skip_serializing_none]
583#[derive(Deserialize, Serialize)]
584pub struct ActionAffordance<Other: ExtendableThing> {
585    /// The interaction affordance.
586    #[serde(flatten)]
587    pub interaction: InteractionAffordance<Other>,
588
589    /// The input data schema of the action.
590    pub input: Option<DataSchemaFromOther<Other>>,
591
592    /// The output data schema of the action.
593    pub output: Option<DataSchemaFromOther<Other>>,
594
595    /// Whether the action is safe or not.
596    ///
597    /// In case it is `true`, when the action is invoked there is no internal state that is being
598    /// changed.
599    #[serde(default)]
600    pub safe: bool,
601
602    /// Whether the action is idempotent or not.
603    ///
604    /// In case it is `true`, the action can be called repeatedly with the same result based on the
605    /// same input.
606    #[serde(default)]
607    pub idempotent: bool,
608
609    /// Whether the action is synchronous or not.
610    ///
611    /// A synchronous action means that the response of action contains all the information about
612    /// the result of the action and no further querying about the status of the action is needed.
613    ///
614    /// If this is `None`, no claim on the synchronicity of the action can be made.
615    pub synchronous: Option<bool>,
616
617    /// Action affordance extension
618    #[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/// An affordance that describes an event source.
681#[skip_serializing_none]
682#[derive(Deserialize, Serialize)]
683pub struct EventAffordance<Other: ExtendableThing> {
684    /// The interaction affordance.
685    #[serde(flatten)]
686    pub interaction: InteractionAffordance<Other>,
687
688    /// Data that needs to be passed upon subscription.
689    pub subscription: Option<DataSchemaFromOther<Other>>,
690
691    /// Data schema of the messages pushed by the `Thing`.
692    pub data: Option<DataSchemaFromOther<Other>>,
693
694    /// Data schema of the responsed messages sent by the consumer in a response to a data message.
695    pub data_response: Option<DataSchemaFromOther<Other>>,
696
697    /// Data that needs to be passed to cancel a subscription.
698    pub cancellation: Option<DataSchemaFromOther<Other>>,
699
700    /// Event affordance extension.
701    #[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/// Metadata of a `Thing` that provides version information about the _Thing Description_ document.
761#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
762pub struct VersionInfo {
763    /// The version indicator of this _Thing Description_ instance.
764    pub instance: String,
765
766    /// The version indicator of the underlying _Thing Model_.
767    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/// Metadata that describes the data format used.
784#[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    /// JSON-LD keyword to label the object with semantic tags or types.
790    #[serde(rename = "@type", default)]
791    #[serde_as(as = "Option<OneOrMany<_>>")]
792    pub attype: Option<Vec<String>>,
793
794    /// Human-readable title to be displayed
795    pub title: Option<String>,
796
797    /// Multi-language translations of the title
798    pub titles: Option<MultiLanguage>,
799
800    /// Human-readable additional information
801    pub description: Option<String>,
802
803    /// Multi-language translations of the description
804    pub descriptions: Option<MultiLanguage>,
805
806    /// A constant value for the data schema.
807    #[serde(rename = "const")]
808    pub constant: Option<Value>,
809
810    /// A default value for the data schema.
811    pub default: Option<Value>,
812
813    /// Unit information used for the data schema (e.g. Km, g, m/s^2)
814    pub unit: Option<String>,
815
816    /// Used to ensure that the data is valid against one of the specified schemas.
817    pub one_of: Option<Vec<Self>>,
818
819    /// A restricted set of values.
820    #[serde(rename = "enum")]
821    pub enumeration: Option<Vec<Value>>,
822
823    /// Indicates if the property interaction value is read only.
824    #[serde(default)]
825    pub read_only: bool,
826
827    /// Indicates if the property interaction value is write only.
828    #[serde(default)]
829    pub write_only: bool,
830
831    /// Allows validation based on a format pattern such as "date-time", "email", "uri".
832    pub format: Option<String>,
833
834    /// The JSON-based subtype of the data schema.
835    #[serde(flatten)]
836    pub subtype: Option<DataSchemaSubtype<DS, AS, OS>>,
837
838    /// Data schema extension.
839    #[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/// A JSON-based data schema subtype.
850#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
851#[serde(tag = "type", rename_all = "lowercase")]
852pub enum DataSchemaSubtype<DS, AS, OS> {
853    /// A JSON array metadata.
854    Array(ArraySchema<DS, AS, OS>),
855
856    /// A boolean.
857    Boolean,
858
859    /// A number metadata.
860    Number(NumberSchema),
861
862    /// An integer metadata.
863    Integer(IntegerSchema),
864
865    /// A JSON object metadata.
866    Object(ObjectSchema<DS, AS, OS>),
867
868    /// A string metadata.
869    String(StringSchema),
870
871    /// A JSON null.
872    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/// A JSON array metadata.
893#[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    /// The characteristics of the JSON array.
902    ///
903    /// An item has a different semantic than a `Vec` of one item:
904    ///
905    /// - a _single_ item `T` represents an array of element where every element must follow the
906    /// schema defined by `T`;
907    /// - a `Vec` of one element `T` represent a tuple of one single element, which must follow the
908    /// schema defined by `T`.
909    ///
910    /// In general, using a `Vec` of data schemas expresses a tuple of elements with a 1:1
911    /// correspondence.
912    #[serde(skip_serializing_if = "Option::is_none")]
913    pub items: Option<BoxedElemOrVec<DataSchema<DS, AS, OS>>>,
914
915    /// The minimum number of items that have to be in the JSON array.
916    pub min_items: Option<u32>,
917
918    /// The maximum number of items that have to be in the JSON array.
919    pub max_items: Option<u32>,
920
921    /// Array schema extension.
922    #[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/// A helper enum to represent an inclusive or exclusive maximum value.
956#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
957pub enum Maximum<T> {
958    /// An inclusive maximum value.
959    #[serde(rename = "maximum")]
960    Inclusive(T),
961
962    /// An exclusive maximum value.
963    #[serde(rename = "exclusiveMaximum")]
964    Exclusive(T),
965}
966
967/// A helper enum to represent an inclusive or exclusive minimum value.
968#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
969pub enum Minimum<T> {
970    /// An inclusive minimum value.
971    #[serde(rename = "minimum")]
972    Inclusive(T),
973
974    /// An exclusive minimum value.
975    #[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            /// Returns `true` if value is `NaN`.
1090            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/// A number metadata.
1110#[skip_serializing_none]
1111#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
1112#[serde(rename_all = "camelCase")]
1113pub struct NumberSchema {
1114    /// The higher limit of the value.
1115    #[serde(flatten)]
1116    pub maximum: Option<Maximum<f64>>,
1117
1118    /// The lower limit of the value.
1119    #[serde(flatten)]
1120    pub minimum: Option<Minimum<f64>>,
1121
1122    /// It adds the requirement that the numeric value must be a multiple of this.
1123    pub multiple_of: Option<f64>,
1124}
1125
1126/// An integer metadata.
1127#[skip_serializing_none]
1128#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1129#[serde(rename_all = "camelCase")]
1130// FIXME: we should probably use a Decimal type
1131pub struct IntegerSchema {
1132    /// The higher limit of the value.
1133    #[serde(flatten)]
1134    pub maximum: Option<Maximum<i64>>,
1135
1136    /// The lower limit of the value.
1137    #[serde(flatten)]
1138    pub minimum: Option<Minimum<i64>>,
1139
1140    /// It adds the requirement that the numeric value must be a multiple of this.
1141    pub multiple_of: Option<NonZeroU64>,
1142}
1143
1144/// A JSON object metadata.
1145#[skip_serializing_none]
1146#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1147pub struct ObjectSchema<DS, AS, OS> {
1148    /// Data schema nested definitions.
1149    pub properties: Option<HashMap<String, DataSchema<DS, AS, OS>>>,
1150
1151    /// Defines which members of the object type are mandatory.
1152    pub required: Option<Vec<String>>,
1153
1154    /// Object schema extension.
1155    #[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/// A string metadata
1193#[skip_serializing_none]
1194#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1195#[serde(rename_all = "camelCase")]
1196pub struct StringSchema {
1197    /// The minimum length of a string.
1198    pub min_length: Option<u32>,
1199
1200    /// The maximum length of a string.
1201    pub max_length: Option<u32>,
1202
1203    /// A regular expression to express constraints of the string value. The regular expression
1204    /// must follow the [ECMA-262](https://www.w3.org/TR/wot-thing-description11/#bib-ecma-262)
1205    /// dialect.
1206    // TODO: this should be a validated against EcmaScript dialect of regexes
1207    pub pattern: Option<String>,
1208
1209    /// The encoding used to store the contents, as specified in [RFC
1210    /// 2045](https://www.rfc-editor.org/rfc/rfc2045).
1211    // TODO: this should be validated against RFC 2045
1212    pub content_encoding: Option<String>,
1213
1214    /// the MIME type of the contents of a string value, as described in [RFC
1215    /// 2046](https://www.rfc-editor.org/rfc/rfc2046).
1216    // TODO: this should be validated against RFC 2046
1217    pub content_media_type: Option<String>,
1218}
1219
1220/// The configuration of a security mechanism.
1221#[serde_as]
1222#[skip_serializing_none]
1223#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1224pub struct SecurityScheme {
1225    /// JSON-LD keyword to label the object with semantic tags or types.
1226    #[serde(rename = "@type", default)]
1227    #[serde_as(as = "Option<OneOrMany<_>>")]
1228    pub attype: Option<Vec<String>>,
1229
1230    /// Human-readable additional information
1231    pub description: Option<String>,
1232
1233    /// Multi-language translations of the description
1234    pub descriptions: Option<MultiLanguage>,
1235
1236    /// URI of the proxy server this security configuration provides access to. If `None`, the
1237    /// corresponding security configuration is for the endpoint.
1238    // FIXME: use AnyURI
1239    pub proxy: Option<String>,
1240
1241    /// The security scheme subtype.
1242    #[serde(flatten)]
1243    pub subtype: SecuritySchemeSubtype,
1244}
1245
1246/// A pre-defined security scheme subtype.
1247#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1248#[serde(tag = "scheme", rename_all = "lowercase")]
1249pub enum KnownSecuritySchemeSubtype {
1250    /// No authentication or other mechanism required to access the resource.
1251    #[default]
1252    NoSec,
1253
1254    /// The security parameters are going to be negotiated by the underlying protocols at runtime.
1255    Auto,
1256
1257    /// A combination of security schemes.
1258    Combo(ComboSecurityScheme),
1259
1260    /// Basic Authentication ([RFC7617](https://httpwg.org/specs/rfc7617.html)) security
1261    /// configuration
1262    Basic(BasicSecurityScheme),
1263
1264    /// Digest Access Authentication ([RFC7616](https://httpwg.org/specs/rfc7616.html)) security
1265    /// configuration
1266    Digest(DigestSecurityScheme),
1267
1268    /// Bearer Token ([RFC6750](https://www.rfc-editor.org/rfc/rfc6750)) security configuration
1269    Bearer(BearerSecurityScheme),
1270
1271    /// Pre-shared key authentication security configuration.
1272    Psk(PskSecurityScheme),
1273
1274    /// OAuth 2.0 authentication security configuration for systems conformant with
1275    /// [RFC6749](https://www.rfc-editor.org/rfc/rfc6749),
1276    /// [RFC8252](https://www.rfc-editor.org/rfc/rfc8252) and (for the device flow)
1277    /// [RFC8628](https://www.rfc-editor.org/rfc/rfc8628)
1278    OAuth2(OAuth2SecurityScheme),
1279
1280    /// API key authentication security configuration.
1281    ApiKey(ApiKeySecurityScheme),
1282}
1283
1284/// Custom security scheme subtype
1285#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1286pub struct UnknownSecuritySchemeSubtype {
1287    /// The name of the security scheme.
1288    pub scheme: String,
1289
1290    /// The inner data of the security scheme.
1291    #[serde(flatten)]
1292    pub data: Value,
1293}
1294
1295/// A security scheme subtype.
1296#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
1297#[serde(untagged)]
1298pub enum SecuritySchemeSubtype {
1299    /// Pre-defined security scheme subtype.
1300    Known(KnownSecuritySchemeSubtype),
1301
1302    /// Custom security scheme subtype
1303    Unknown(UnknownSecuritySchemeSubtype),
1304}
1305
1306impl Default for SecuritySchemeSubtype {
1307    fn default() -> Self {
1308        Self::Known(KnownSecuritySchemeSubtype::default())
1309    }
1310}
1311
1312/// A combination of security schemes.
1313#[serde_as]
1314#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1315#[serde(rename_all = "camelCase")]
1316pub enum ComboSecurityScheme {
1317    /// Two or more strings identifying other named security scheme definitions, any one of which,
1318    /// when satisfied, will allow access.
1319    OneOf(#[serde_as(as = "OneOrMany<_>")] Vec<String>),
1320
1321    /// Two or more strings identifying other named security scheme definitions, all of which must
1322    /// be satisfied for access.
1323    AllOf(#[serde_as(as = "OneOrMany<_>")] Vec<String>),
1324}
1325
1326/// Basic Authentication ([RFC7617](https://httpwg.org/specs/rfc7617.html)) security configuration
1327#[skip_serializing_none]
1328#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1329pub struct BasicSecurityScheme {
1330    /// The location of security authentication information.
1331    #[serde(rename = "in", default = "SecurityAuthenticationLocation::header")]
1332    pub location: SecurityAuthenticationLocation,
1333
1334    /// Name for query, header, cookie, or uri parameters.
1335    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/// The location of security authentication information.
1348#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1349#[serde(rename_all = "lowercase")]
1350pub enum SecurityAuthenticationLocation {
1351    /// The parameter will be given in a header provided by the protocol, with the name of the
1352    /// header provided by the value of `name`.
1353    Header,
1354
1355    /// The parameter will be appended to the URI as a query parameter, with the name of the query
1356    /// parameter provided by `name`.
1357    Query,
1358
1359    /// The parameter will be provided in the body of the request payload, with the data schema
1360    /// element used provided by `name`.
1361    Body,
1362
1363    /// The parameter is stored in a cookie identified by the value of `name`.
1364    Cookie,
1365
1366    /// The parameter is embedded in the URI itself, which is encoded in the relevant interaction
1367    /// using a URI template variable defined by the value of `name`.
1368    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/// Digest Access Authentication ([RFC7616](https://httpwg.org/specs/rfc7616.html)) security configuration
1382#[skip_serializing_none]
1383#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1384pub struct DigestSecurityScheme {
1385    /// Quality of protection.
1386    pub qop: QualityOfProtection,
1387
1388    /// The location of security authentication information.
1389    #[serde(rename = "in", default = "SecurityAuthenticationLocation::header")]
1390    pub location: SecurityAuthenticationLocation,
1391
1392    /// Name for query, header, cookie, or uri parameters.
1393    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/// Quality of protection, as defined in [RFC2617](https://www.rfc-editor.org/rfc/rfc2617).
1407#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1408#[serde(rename_all = "kebab-case")]
1409pub enum QualityOfProtection {
1410    /// Protection by authentication.
1411    #[default]
1412    Auth,
1413
1414    /// Protection by authentication with integrity protection.
1415    AuthInt,
1416}
1417
1418/// API key authentication security configuration.
1419#[skip_serializing_none]
1420#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1421pub struct ApiKeySecurityScheme {
1422    /// The location of security authentication information.
1423    #[serde(rename = "in", default = "SecurityAuthenticationLocation::query")]
1424    pub location: SecurityAuthenticationLocation,
1425
1426    /// Name for query, header, cookie, or uri parameters.
1427    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/// Pre-shared key authentication security configuration.
1440#[skip_serializing_none]
1441#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1442pub struct BearerSecurityScheme {
1443    /// URI of the authorization server.
1444    // FIXME: use AnyURI
1445    pub authorization: Option<String>,
1446
1447    /// Encoding, encryption, or digest algorithm.
1448    #[serde(default = "BearerSecurityScheme::default_alg")]
1449    pub alg: Cow<'static, str>,
1450
1451    /// Format of security authentication information.
1452    #[serde(default = "BearerSecurityScheme::default_format")]
1453    pub format: Cow<'static, str>,
1454
1455    /// The location of security authentication information.
1456    #[serde(rename = "in", default = "SecurityAuthenticationLocation::header")]
1457    pub location: SecurityAuthenticationLocation,
1458
1459    /// Name for query, header, cookie, or uri parameters.
1460    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/// Pre-shared key authentication security configuration.
1486#[skip_serializing_none]
1487#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1488pub struct PskSecurityScheme {
1489    /// Identifier providing information useful for selection or confirmation.
1490    pub identity: Option<String>,
1491}
1492
1493/// OAuth 2.0 authentication security configuration for systems conformant with
1494/// [RFC6749](https://www.rfc-editor.org/rfc/rfc6749),
1495/// [RFC8252](https://www.rfc-editor.org/rfc/rfc8252) and (for the device flow)
1496/// [RFC8628](https://www.rfc-editor.org/rfc/rfc8628)
1497#[serde_as]
1498#[skip_serializing_none]
1499#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1500pub struct OAuth2SecurityScheme {
1501    /// URI of the authorization server. In the case of the device flow, the URI provided for the
1502    /// authorization value refers to the device authorization endpoint
1503    /// ([RFC8628](https://www.rfc-editor.org/rfc/rfc8628)).
1504    // FIXME: use AnyURI
1505    pub authorization: Option<String>,
1506
1507    /// URI of the token server.
1508    // FIXME: use AnyURI
1509    pub token: Option<String>,
1510
1511    /// URI of the refresh server.
1512    // FIXME: use AnyURI
1513    pub refresh: Option<String>,
1514
1515    /// Set of authorization scope identifiers.
1516    ///
1517    /// These are provided in tokens returned by an authorization server and associated with forms
1518    /// in order to identify what resources a client may access and how.
1519    #[serde(default)]
1520    #[serde_as(as = "Option<OneOrMany<_>>")]
1521    pub scopes: Option<Vec<String>>,
1522
1523    /// Authorization flow.
1524    pub flow: String,
1525}
1526
1527impl OAuth2SecurityScheme {
1528    /// Creates a new default value with the given `flow`.
1529    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/// A link to an arbitrary resource.
1542#[serde_as]
1543#[skip_serializing_none]
1544#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
1545pub struct Link {
1546    /// Target IRI of a link or submission target of a form.
1547    pub href: String,
1548
1549    /// Target attribute providing a hint indicating what the media type
1550    /// ([RFC2046](https://www.rfc-editor.org/rfc/rfc2046)) of the result of dereferencing the link
1551    /// should be.
1552    #[serde(rename = "type")]
1553    pub ty: Option<String>,
1554
1555    /// A link relation type identifies the semantics of a link.
1556    pub rel: Option<String>,
1557
1558    /// Overrides the link context with the given URI or IRI.
1559    ///
1560    /// By default the link context is the Thing itself identified by its `id`.
1561    // FIXME: use AnyURI
1562    pub anchor: Option<String>,
1563
1564    /// One or more sizes for the referenced icon.
1565    ///
1566    /// This is only applicable for relation type "icon". The value pattern follows {Height}x{Width} (e.g., "16x16", "16x16 32x32").
1567    pub sizes: Option<String>,
1568
1569    /// The language of a linked document.
1570    #[serde(default)]
1571    #[serde_as(as = "Option<OneOrMany<_>>")]
1572    pub hreflang: Option<Vec<LanguageTag<String>>>,
1573}
1574
1575/// The representation of an operation over a Thing.
1576#[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    /// The semantic intention of performing the operation(s) described by the form.
1582    #[serde(default, skip_serializing_if = "DefaultedFormOperations::is_default")]
1583    pub op: DefaultedFormOperations,
1584
1585    /// Target IRI of a link or submission target of a form.
1586    // FIXME: use AnyURI
1587    pub href: String,
1588
1589    /// A content type.
1590    ///
1591    /// It is based on a media type (e.g., text/plain) and potential parameters (e.g.,
1592    /// charset=utf-8) for the media type ([RFC2046](https://www.rfc-editor.org/rfc/rfc2046)).
1593    pub content_type: Option<String>,
1594
1595    /// Content coding values indicate an encoding transformation that has been or can be applied
1596    /// to a representation.
1597    ///
1598    /// Content codings are primarily used to allow a representation to be compressed or otherwise
1599    /// usefully transformed without losing the identity of its underlying media type and without
1600    /// loss of information.
1601    ///
1602    /// Examples of content coding include "gzip", "deflate", etc. .
1603    // TODO: check if the subset of possible values is limited by the [IANA HTTP content coding
1604    // registry](https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding).
1605    pub content_coding: Option<String>,
1606
1607    /// The mechanism by which an interaction will be accomplished for a given protocol when there
1608    /// are multiple options.
1609    pub subprotocol: Option<String>,
1610
1611    /// Set of security definition names, chosen from those defined in
1612    /// [`security_definitions`](Thing::security_definitions). These must all be satisfied for
1613    /// access to resources.
1614    // FIXME: use variant names of KnownSecuritySchemeSubtype + "other" string variant
1615    #[serde(default)]
1616    #[serde_as(as = "Option<OneOrMany<_>>")]
1617    pub security: Option<Vec<String>>,
1618
1619    /// Set of authorization scope identifiers.
1620    ///
1621    /// The values associated with a form should be chosen from those defined in an
1622    /// [`OAuth2SecurityScheme`] active on that form.
1623    #[serde(default)]
1624    #[serde_as(as = "Option<OneOrMany<_>>")]
1625    pub scopes: Option<Vec<String>>,
1626
1627    /// The expected response from the call to the resource.
1628    ///
1629    /// The response name contains metadata that is only valid for the primary response messages
1630    pub response: Option<ExpectedResponse<Other::ExpectedResponse>>,
1631
1632    /// Additional expected responses.
1633    #[serde(default)]
1634    #[serde_as(as = "Option<OneOrMany<_>>")]
1635    pub additional_responses: Option<Vec<AdditionalExpectedResponse>>,
1636
1637    /// Form extension.
1638    #[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/// The semantic intention of an operation.
1665#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
1666#[serde(rename_all = "lowercase")]
1667pub enum FormOperation {
1668    /// Read a property.
1669    ReadProperty,
1670
1671    /// Update a property.
1672    WriteProperty,
1673
1674    /// Observe a property.
1675    ///
1676    /// Identifies an operation to be notified with new data when the property is updated.
1677    ObserveProperty,
1678
1679    /// Unobserve a property.
1680    ///
1681    /// Stops the notification from a previously observed property.
1682    UnobserveProperty,
1683
1684    /// Perform an action.
1685    InvokeAction,
1686
1687    /// Get the status of an action.
1688    QueryAction,
1689
1690    /// Cancel an ongoing action.
1691    CancelAction,
1692
1693    /// Subscribe to an event.
1694    ///
1695    /// Identifies an operation to be notified when an event occurs.
1696    SubscribeEvent,
1697
1698    /// Unsubscribe from an event.
1699    ///
1700    /// Stops the notification from a previously subscribed event.
1701    UnsubscribeEvent,
1702
1703    /// Read all the properties in a single interaction.
1704    ReadAllProperties,
1705
1706    /// Update all the properties in a single interaction.
1707    WriteAllProperties,
1708
1709    /// Read multiple selected properties in a single interaction.
1710    ReadMultipleProperties,
1711
1712    /// Update multiple selected properties in a single interaction.
1713    WriteMultipleProperties,
1714
1715    /// Observe all the properties in a single interaction.
1716    ObserveAllProperties,
1717
1718    /// Unobserve all the properties in a single interaction.
1719    UnobserveAllProperties,
1720
1721    /// Subscribe to all events in a single interaction.
1722    SubscribeAllEvents,
1723
1724    /// Unsubscribe from all events in a single interaction.
1725    UnsubscribeAllEvents,
1726
1727    /// Get the status of all actions in a single interaction.
1728    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/// A default or custom set of Form operations.
1759///
1760/// A `Form` has a different default `op` field depending on its context. With this, it is possible
1761/// to specify a _default_ operation independently from the context.
1762///
1763/// Note: an instance of this enum should not be serialized if it is a `Default` value.
1764#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
1765pub enum DefaultedFormOperations {
1766    /// The default operation depending on the context.
1767    #[default]
1768    Default,
1769
1770    /// A custom set of operations.
1771    Custom(Vec<FormOperation>),
1772}
1773
1774impl DefaultedFormOperations {
1775    /// Returns `true` if the operation is a [`Default`](DefaultedFormOperations::Default) value.
1776    #[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/// The expected response message for the primary response.
1809#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1810#[serde(rename_all = "camelCase")]
1811pub struct ExpectedResponse<Other> {
1812    /// A content type.
1813    ///
1814    /// It is based on a media type (e.g., text/plain) and potential parameters (e.g.,
1815    /// charset=utf-8) for the media type ([RFC2046](https://www.rfc-editor.org/rfc/rfc2046)).
1816    pub content_type: String,
1817
1818    /// Expected response extension.
1819    #[serde(flatten)]
1820    pub other: Other,
1821}
1822
1823/// The expected response message for additional responses.
1824#[skip_serializing_none]
1825#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
1826#[serde(rename_all = "camelCase")]
1827pub struct AdditionalExpectedResponse {
1828    /// It is `true` if an additional response should not be considered an error.
1829    #[serde(default = "bool_false", skip_serializing_if = "is_false")]
1830    pub success: bool,
1831
1832    /// A content type.
1833    ///
1834    /// It is based on a media type (e.g., text/plain) and potential parameters (e.g.,
1835    /// charset=utf-8) for the media type ([RFC2046](https://www.rfc-editor.org/rfc/rfc2046)).
1836    pub content_type: Option<String>,
1837
1838    /// The output data schema for an additional response if it differs from the default output data schema.
1839    ///
1840    /// It is the name of a previous definition given in the
1841    /// [`schema_definitions`](Thing::schema_definitions).
1842    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}