wot_td/builder/
affordance.rs

1//! The builder elements related to affordances.
2//!
3//! The affordances in the specification are expressed in term of object inheritance. The builders
4//! in this module mimic that behavior using traits, which must be imported in the context to work.
5//!
6//! See the examples related to the specific affordance bulders for more information.
7
8use alloc::{string::String, vec::Vec};
9use core::ops::Not;
10
11use hashbrown::HashMap;
12use serde_json::Value;
13
14use crate::{
15    extend::{Extend, Extendable, ExtendableThing},
16    thing::{
17        ActionAffordance, DataSchema, DefaultedFormOperations, EventAffordance, Form,
18        FormOperation, InteractionAffordance, PropertyAffordance, SecurityScheme,
19    },
20};
21
22use super::{
23    data_schema::{
24        buildable_data_schema_delegate, impl_inner_delegate_schema_builder_like_integer,
25        impl_inner_delegate_schema_builder_like_number,
26        impl_inner_delegate_schema_builder_like_object,
27        impl_inner_delegate_schema_builder_like_tuple, impl_inner_delegate_schema_builder_like_vec,
28        uri_variables_contains_arrays_objects, BuildableDataSchema, DataSchemaBuilder,
29        EnumerableDataSchema, IntegerDataSchemaBuilderLike, NumberDataSchemaBuilderLike,
30        ObjectDataSchemaBuilderLike, PartialDataSchema, PartialDataSchemaBuilder,
31        ReadableWriteableDataSchema, SpecializableDataSchema, TupleDataSchemaBuilderLike,
32        UncheckedDataSchemaFromOther, UncheckedDataSchemaMap, UnionDataSchema,
33        VecDataSchemaBuilderLike,
34    },
35    human_readable_info::{
36        impl_delegate_buildable_hr_info, BuildableHumanReadableInfo, HumanReadableInfo,
37    },
38    AffordanceType, Error, Extended, FormBuilder, MultiLanguageBuilder, ToExtend,
39};
40
41/// A conversion into an _usable_ form of a value.
42///
43/// This is extremely handy to convert between the same kind with different generics. [`From`] and
44/// [`Into`] cannot be used directly because of some blanket implementations that cause conflicts.
45pub trait IntoUsable<T>: Sized {
46    /// Converts `self` into an _usable_ value with a different type.
47    fn into_usable(self) -> T;
48}
49
50pub(super) struct AffordanceBuilder<Affordance> {
51    pub(super) name: String,
52    pub(super) affordance: Affordance,
53}
54
55/// An interface for a buildable version of an [`InteractionAffordance`](crate::thing::InteractionAffordance).
56///
57/// In order to model the specification, each type that can be created using a builder pattern and
58/// that _behaves_ like an `InteractionAffordance` should implement this trait.
59///
60/// # Notes
61///
62/// This trait *should not* be implemented directly, even if it is not sealed.
63pub trait BuildableInteractionAffordance<Other: ExtendableThing> {
64    /// Adds a new `Form`.
65    ///
66    /// It takes a function that accepts an _incomplete_ [`FormBuilder`] and must return a
67    /// _complete_ one.
68    ///
69    /// # Example
70    /// ```
71    /// # use serde_json::json;
72    /// use wot_td::builder::affordance::BuildableInteractionAffordance;
73    /// # use wot_td::thing::Thing;
74    ///
75    /// let thing = Thing::builder("Thing name")
76    ///     .finish_extend()
77    ///     .action("aff", |b| b.form(|b| b.href("href")))
78    ///     .build()
79    ///     .unwrap();
80    ///
81    /// assert_eq!(
82    ///     serde_json::to_value(thing).unwrap(),
83    ///     json!({
84    ///         "title": "Thing name",
85    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
86    ///         "actions": {
87    ///             "aff": {
88    ///                 "forms": [{
89    ///                     "href": "href",
90    ///                 }],
91    ///                 "idempotent": false,
92    ///                 "safe": false,
93    ///             },
94    ///         },
95    ///         "security": [],
96    ///         "securityDefinitions": {},
97    ///     })
98    /// );
99    /// ```
100    fn form<F, R>(self, f: F) -> Self
101    where
102        F: FnOnce(FormBuilder<Other, (), <Other::Form as Extendable>::Empty>) -> R,
103        R: Into<FormBuilder<Other, String, Other::Form>>,
104        Other::Form: Extendable;
105
106    /// Adds a new _URI variable_.
107    ///
108    /// It takes a function that accepts a `DataSchema` builder and must return a
109    /// type convertible into a `DataSchema`.
110    /// ```
111    /// # use serde_json::json;
112    /// use wot_td::builder::affordance::BuildableInteractionAffordance;
113    /// # use wot_td::{builder::data_schema::SpecializableDataSchema, thing::Thing};
114    ///
115    /// let thing = Thing::builder("Thing name")
116    ///     .finish_extend()
117    ///     .action("aff", |b| {
118    ///         b.uri_variable("myvar", |b| b.finish_extend().number())
119    ///     })
120    ///     .build()
121    ///     .unwrap();
122    ///
123    /// assert_eq!(
124    ///     serde_json::to_value(thing).unwrap(),
125    ///     json!({
126    ///         "title": "Thing name",
127    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
128    ///         "actions": {
129    ///             "aff": {
130    ///                 "forms": [],
131    ///                 "uriVariables": {
132    ///                     "myvar": {
133    ///                         "type": "number",
134    ///                         "readOnly": false,
135    ///                         "writeOnly": false,
136    ///                     },
137    ///                 },
138    ///                 "idempotent": false,
139    ///                 "safe": false,
140    ///             },
141    ///         },
142    ///         "security": [],
143    ///         "securityDefinitions": {},
144    ///     })
145    /// );
146    /// ```
147    fn uri_variable<F, T>(self, name: impl Into<String>, f: F) -> Self
148    where
149        F: FnOnce(
150            DataSchemaBuilder<
151                <Other::DataSchema as Extendable>::Empty,
152                Other::ArraySchema,
153                Other::ObjectSchema,
154                ToExtend,
155            >,
156        ) -> T,
157        T: Into<UncheckedDataSchemaFromOther<Other>>,
158        Other::DataSchema: Extendable;
159}
160
161/// _Partial_ variant of an [`InteractionAffordanceBuilder`].
162///
163/// This variant is necessary for building a [`PropertyAffordance`], which is composed of a set of
164/// _human readable_ fields shared between [`InteractionAffordance`] and [`DataSchema`].
165pub(crate) struct PartialInteractionAffordanceBuilder<
166    Other: ExtendableThing,
167    OtherInteractionAffordance,
168> {
169    pub(super) forms: Vec<FormBuilder<Other, String, Other::Form>>,
170    pub(super) uri_variables: HashMap<String, UncheckedDataSchemaFromOther<Other>>,
171
172    /// Partial interaction affordance extension.
173    pub other: OtherInteractionAffordance,
174}
175
176impl<Other, OtherInteractionAffordance> Default
177    for PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>
178where
179    Other: ExtendableThing,
180    OtherInteractionAffordance: Default,
181{
182    fn default() -> Self {
183        Self {
184            forms: Default::default(),
185            uri_variables: Default::default(),
186            other: Default::default(),
187        }
188    }
189}
190
191impl<Other>
192    PartialInteractionAffordanceBuilder<Other, <Other::InteractionAffordance as Extendable>::Empty>
193where
194    Other: ExtendableThing,
195    Other::InteractionAffordance: Extendable,
196{
197    pub(crate) fn empty() -> Self {
198        Self {
199            forms: Default::default(),
200            uri_variables: Default::default(),
201            other: Other::InteractionAffordance::empty(),
202        }
203    }
204}
205
206impl<Other: ExtendableThing, OtherInteractionAffordance>
207    PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>
208{
209    /// Extends the current type, passing a closure that returns `T`.
210    fn ext_with<F, T>(
211        self,
212        f: F,
213    ) -> PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance::Target>
214    where
215        OtherInteractionAffordance: Extend<T>,
216        F: FnOnce() -> T,
217    {
218        let Self {
219            forms,
220            uri_variables,
221            other,
222        } = self;
223        let other = other.ext_with(f);
224        PartialInteractionAffordanceBuilder {
225            forms,
226            uri_variables,
227            other,
228        }
229    }
230}
231
232impl<Other, OtherInteractionAffordance>
233    IntoUsable<PartialInteractionAffordanceBuilder<Other, Other::InteractionAffordance>>
234    for PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>
235where
236    Other: ExtendableThing,
237    OtherInteractionAffordance: Into<Other::InteractionAffordance>,
238{
239    fn into_usable(
240        self,
241    ) -> PartialInteractionAffordanceBuilder<Other, Other::InteractionAffordance> {
242        let Self {
243            forms,
244            uri_variables,
245            other,
246        } = self;
247
248        let other = other.into();
249        PartialInteractionAffordanceBuilder {
250            forms,
251            uri_variables,
252            other,
253        }
254    }
255}
256
257/// A builder for [`InteractionAffordance`].
258#[derive(Default)]
259pub(crate) struct InteractionAffordanceBuilder<Other: ExtendableThing, OtherInteractionAffordance> {
260    pub(super) partial: PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>,
261    pub(super) info: HumanReadableInfo,
262}
263
264impl<Other: ExtendableThing, OtherInteractionAffordance>
265    InteractionAffordanceBuilder<Other, OtherInteractionAffordance>
266{
267    /// Extends the current type, passing a closure that returns `T`.
268    pub(crate) fn ext_with<F, T>(
269        self,
270        f: F,
271    ) -> InteractionAffordanceBuilder<Other, OtherInteractionAffordance::Target>
272    where
273        OtherInteractionAffordance: Extend<T>,
274        F: FnOnce() -> T,
275    {
276        let Self { partial, info } = self;
277        let partial = partial.ext_with(f);
278        InteractionAffordanceBuilder { partial, info }
279    }
280
281    /// Extend the current extension with an additional element
282    #[cfg(test)]
283    #[inline]
284    pub(crate) fn ext<T>(
285        self,
286        t: T,
287    ) -> InteractionAffordanceBuilder<Other, OtherInteractionAffordance::Target>
288    where
289        OtherInteractionAffordance: Extend<T>,
290    {
291        self.ext_with(|| t)
292    }
293}
294
295impl<Other> InteractionAffordanceBuilder<Other, Other::InteractionAffordance>
296where
297    Other: ExtendableThing,
298    Other::InteractionAffordance: Extendable,
299{
300    pub(crate) fn empty(
301    ) -> InteractionAffordanceBuilder<Other, <Other::InteractionAffordance as Extendable>::Empty>
302    {
303        InteractionAffordanceBuilder {
304            partial: PartialInteractionAffordanceBuilder::empty(),
305            info: Default::default(),
306        }
307    }
308}
309
310impl<Other, OtherInteractionAffordance> BuildableInteractionAffordance<Other>
311    for PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>
312where
313    Other: ExtendableThing,
314    Other::Form: Extendable,
315{
316    fn form<F, R>(mut self, f: F) -> Self
317    where
318        F: FnOnce(FormBuilder<Other, (), <Other::Form as Extendable>::Empty>) -> R,
319        R: Into<FormBuilder<Other, String, Other::Form>>,
320    {
321        self.forms.push(f(FormBuilder::new()).into());
322        self
323    }
324
325    fn uri_variable<F, T>(mut self, name: impl Into<String>, f: F) -> Self
326    where
327        F: FnOnce(
328            DataSchemaBuilder<
329                <Other::DataSchema as Extendable>::Empty,
330                Other::ArraySchema,
331                Other::ObjectSchema,
332                ToExtend,
333            >,
334        ) -> T,
335        T: Into<UncheckedDataSchemaFromOther<Other>>,
336        Other::DataSchema: Extendable,
337    {
338        self.uri_variables.insert(
339            name.into(),
340            f(DataSchemaBuilder::<Other::DataSchema, _, _, _>::empty()).into(),
341        );
342        self
343    }
344}
345
346macro_rules! impl_buildable_interaction_affordance {
347    ($($ty:ident $( <$($generic:ident $(: $bound:path)?),+> )? on $($interaction_path:ident).+),+ $(,)?) => {
348        $(
349            impl $(< $($generic $(: $bound)?),+ >)? BuildableInteractionAffordance<Other> for $ty $(< $($generic),+ >)?
350            where
351                Other::Form: Extendable
352            {
353                fn form<F, R>(mut self, f: F) -> Self
354                where
355                    F: FnOnce(FormBuilder<Other, (), <Other::Form as Extendable>::Empty>) -> R,
356                    R: Into<FormBuilder<Other, String, Other::Form>>,
357                    Other::Form: Extendable,
358                {
359                    self.$($interaction_path).* = self.$($interaction_path).*.form(f);
360                    self
361                }
362
363                fn uri_variable<F, T>(mut self, name: impl Into<String>, f: F) -> Self
364                where
365                    F: FnOnce(DataSchemaBuilder<<Other::DataSchema as Extendable>::Empty, Other::ArraySchema, Other::ObjectSchema, ToExtend>) -> T,
366                    T: Into<UncheckedDataSchemaFromOther<Other>>,
367                    Other::DataSchema: Extendable,
368                {
369                    self.$($interaction_path).* = self.$($interaction_path).*.uri_variable(name, f);
370                    self
371                }
372            }
373        )+
374    };
375}
376
377impl_buildable_interaction_affordance!(
378    InteractionAffordanceBuilder<Other: ExtendableThing, OtherInteractionAffordance> on partial,
379    PropertyAffordanceBuilder<Other: ExtendableThing, DS, OtherInteractionAffordance, OtherPropertyAffordance> on interaction,
380    ActionAffordanceBuilder<Other: ExtendableThing, OtherInteractionAffordance, OtherActionAffordance> on interaction.partial,
381    EventAffordanceBuilder<Other: ExtendableThing, OtherInteractionAffordance, OtherEventAffordance> on interaction.partial,
382);
383
384/// A builder for [`PropertyAffordance`]
385///
386/// A `PropertyAffordanceBuilder` behaves like an interaction affordance builder, a _human readable
387/// info_ builder and a data schema builder. Because of this, you can customize the relative parts
388/// once the traits [`BuildableInteractionAffordance`], [`BuildableHumanReadableInfo`] and
389/// [`SpecializableDataSchema`] (or others related to data schema, see [`DataSchemaBuilder`]
390/// documentation for more information) are in scope.
391///
392/// `PropertyAffordanceBuilder` has three level of extension:
393///
394/// - `PropertyAffordance` through [`ext`]/[`ext_with`]
395/// - `InteractionAffordance` through [`ext_interaction`]/[`ext_interaction_with`]
396/// - `DataSchema` through [`ext_data_schema`]/[`ext_data_schema_with`]
397///
398/// If the _parent_ `ThingBuilder` is extended, it is necessary to explicitly and correctly extend
399/// these three levels for `PropertyAffordanceBuilder`.
400///
401/// Moreover, [`finish_extend_data_schema`] *must* be called in order to make the `DataSchemaBuilder`
402/// specializable and usable in general.
403///
404/// [`DataSchemaBuilder`]: crate::builder::data_schema::DataSchemaBuilder
405/// [`ext`]: Self::ext
406/// [`ext_with`]: Self::ext_with
407/// [`ext_interaction`]: Self::ext_interaction
408/// [`ext_interaction_with`]: Self::ext_interaction_with
409/// [`ext_data_schema`]: Self::ext_data_schema
410/// [`ext_data_schema_with`]: Self::ext_data_schema_with
411/// [`finish_extend_data_schema`]: Self::finish_extend_data_schema
412///
413/// # Example
414///
415/// ```
416/// # use serde::{Deserialize, Serialize};
417/// # use serde_json::json;
418/// # use wot_td::{
419/// #     builder::data_schema::SpecializableDataSchema, extend::ExtendableThing, thing::Thing,
420/// # };
421/// #
422/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
423/// struct ThingExtension {
424///     a_field: String,
425///     another_field: u32,
426/// }
427///
428/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
429/// struct InteractionExtension {
430///     ia_field: f32,
431/// }
432///
433/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
434/// struct PropertyExtension {
435///     prop_field: u32,
436/// }
437///
438/// impl ExtendableThing for ThingExtension {
439///     type InteractionAffordance = InteractionExtension;
440///     type PropertyAffordance = PropertyExtension;
441///     /* Other types set to `()` */
442/// #   type Form = ();
443/// #   type ActionAffordance = ();
444/// #   type EventAffordance = ();
445/// #   type ExpectedResponse = ();
446/// #   type DataSchema = ();
447/// #   type ObjectSchema = ();
448/// #   type ArraySchema = ();
449/// }
450///
451/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
452/// struct AnotherExtension {
453///     another_field_again: Vec<String>,
454/// }
455///
456/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
457/// struct AnotherPropertyExtension {
458///     another_prop_field: String,
459/// }
460///
461/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
462/// struct DataExtension {
463///     data_field: u32,
464/// }
465///
466/// impl ExtendableThing for AnotherExtension {
467///     type PropertyAffordance = AnotherPropertyExtension;
468///     type DataSchema = DataExtension;
469///     /* Other types set to `()` */
470/// #   type InteractionAffordance = ();
471/// #   type ActionAffordance = ();
472/// #   type EventAffordance = ();
473/// #   type Form = ();
474/// #   type ExpectedResponse = ();
475/// #   type ObjectSchema = ();
476/// #   type ArraySchema = ();
477/// }
478///
479/// let thing = Thing::builder("Thing name")
480///     .ext(ThingExtension {
481///         a_field: "hello world".to_string(),
482///         another_field: 42,
483///     })
484///     .ext(AnotherExtension {
485///         another_field_again: vec!["field1".to_string(), "field2".to_string()],
486///     })
487///     .finish_extend()
488///     .property("prop", |b| {
489///         b.ext_interaction(InteractionExtension { ia_field: 42. })
490///             .ext_interaction(())
491///             .ext(PropertyExtension { prop_field: 23 })
492///             .ext(AnotherPropertyExtension {
493///                 another_prop_field: "hello".to_string(),
494///             })
495///             .ext_data_schema(())
496///             .ext_data_schema(DataExtension { data_field: 101 })
497///             .finish_extend_data_schema()
498///             .bool()
499///     })
500///     .build()
501///     .unwrap();
502///
503/// assert_eq!(
504///     serde_json::to_value(thing).unwrap(),
505///     json!({
506///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
507///         "title": "Thing name",
508///         "a_field": "hello world",
509///         "another_field": 42,
510///         "another_field_again": ["field1", "field2"],
511///         "properties": {
512///             "prop": {
513///                 "ia_field": 42.,
514///                 "prop_field": 23,
515///                 "another_prop_field": "hello",
516///                 "data_field": 101,
517///                 "readOnly": false,
518///                 "writeOnly": false,
519///                 "type": "boolean",
520///                 "forms": [],
521///             }
522///         },
523///         "security": [],
524///         "securityDefinitions": {},
525///     })
526/// );
527/// ```
528pub struct PropertyAffordanceBuilder<
529    Other: ExtendableThing,
530    DataSchema,
531    OtherInteractionAffordance,
532    OtherPropertyAffordance,
533> {
534    pub(super) interaction: PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>,
535    pub(super) info: HumanReadableInfo,
536    pub(super) data_schema: DataSchema,
537    pub(super) observable: Option<bool>,
538
539    /// Property affordance extension.
540    pub other: OtherPropertyAffordance,
541}
542
543impl<Other, DataSchema, OtherInteractionAffordance, OtherPropertyAffordance> Default
544    for PropertyAffordanceBuilder<
545        Other,
546        DataSchema,
547        OtherInteractionAffordance,
548        OtherPropertyAffordance,
549    >
550where
551    Other: ExtendableThing,
552    PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>: Default,
553    DataSchema: Default,
554    OtherPropertyAffordance: Default,
555{
556    fn default() -> Self {
557        Self {
558            interaction: Default::default(),
559            info: Default::default(),
560            data_schema: Default::default(),
561            observable: Default::default(),
562            other: Default::default(),
563        }
564    }
565}
566
567/// Builder for [`ActionAffordance`].
568///
569/// A `ActionAffordanceBuilder` behaves like an interaction affordance builder, and because of this
570/// you can customize the relative parts once the traits [`BuildableInteractionAffordance`] and
571/// [`BuildableHumanReadableInfo`] are in scope.
572///
573/// `ActionAffordanceBuilder` has two level of extension:
574///
575/// - `ActionAffordance` through [`ext`]/[`ext_with`]
576/// - `InteractionAffordance` through [`ext_interaction`]/[`ext_interaction_with`]
577///
578/// If the _parent_ `ThingBuilder` is extended, it is necessary to explicitly and correctly extend
579/// these two levels for `ActionAffordanceBuilder`.
580///
581/// [`ext`]: Self::ext
582/// [`ext_with`]: Self::ext_with
583/// [`ext_interaction`]: Self::ext_interaction
584/// [`ext_interaction_with`]: Self::ext_interaction_with
585///
586/// # Example
587///
588/// ```
589/// # use serde::{Deserialize, Serialize};
590/// # use serde_json::json;
591/// # use wot_td::{
592/// #     builder::data_schema::SpecializableDataSchema, extend::ExtendableThing, thing::Thing,
593/// # };
594/// #
595/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
596/// struct ThingExtension {
597///     a_field: String,
598///     another_field: u32,
599/// }
600///
601/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
602/// struct InteractionExtension {
603///     ia_field: f32,
604/// }
605///
606/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
607/// struct ActionExtension {
608///     action_field: u32,
609/// }
610///
611/// impl ExtendableThing for ThingExtension {
612///     type InteractionAffordance = InteractionExtension;
613///     type ActionAffordance = ActionExtension;
614///     /* Other types set to `()` */
615/// #   type Form = ();
616/// #   type PropertyAffordance = ();
617/// #   type EventAffordance = ();
618/// #   type ExpectedResponse = ();
619/// #   type DataSchema = ();
620/// #   type ObjectSchema = ();
621/// #   type ArraySchema = ();
622/// }
623///
624/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
625/// struct AnotherExtension {
626///     another_field_again: Vec<String>,
627/// }
628///
629/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
630/// struct AnotherActionExtension {
631///     another_action_field: String,
632/// }
633///
634/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
635/// struct DataExtension {
636///     data_field: u32,
637/// }
638///
639/// impl ExtendableThing for AnotherExtension {
640///     type ActionAffordance = AnotherActionExtension;
641///     /* Other types set to `()` */
642/// #   type InteractionAffordance = ();
643/// #   type PropertyAffordance = ();
644/// #   type EventAffordance = ();
645/// #   type DataSchema = ();
646/// #   type Form = ();
647/// #   type ExpectedResponse = ();
648/// #   type ObjectSchema = ();
649/// #   type ArraySchema = ();
650/// }
651///
652/// let thing = Thing::builder("Thing name")
653///     .ext(ThingExtension {
654///         a_field: "hello world".to_string(),
655///         another_field: 42,
656///     })
657///     .ext(AnotherExtension {
658///         another_field_again: vec!["field1".to_string(), "field2".to_string()],
659///     })
660///     .finish_extend()
661///     .action("action", |b| {
662///         b.ext_interaction(InteractionExtension { ia_field: 42. })
663///             .ext_interaction(())
664///             .ext(ActionExtension { action_field: 23 })
665///             .ext(AnotherActionExtension {
666///                 another_action_field: "hello".to_string(),
667///             })
668///             .input(|b| b.ext(()).ext(()).finish_extend().number())
669///     })
670///     .build()
671///     .unwrap();
672///
673/// assert_eq!(
674///     serde_json::to_value(thing).unwrap(),
675///     json!({
676///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
677///         "title": "Thing name",
678///         "a_field": "hello world",
679///         "another_field": 42,
680///         "another_field_again": ["field1", "field2"],
681///         "actions": {
682///             "action": {
683///                 "ia_field": 42.,
684///                 "action_field": 23,
685///                 "another_action_field": "hello",
686///                 "input": {
687///                     "type": "number",
688///                     "readOnly": false,
689///                     "writeOnly": false,
690///                 },
691///                 "idempotent": false,
692///                 "safe": false,
693///                 "forms": [],
694///             }
695///         },
696///         "security": [],
697///         "securityDefinitions": {},
698///     })
699/// );
700/// ```
701pub struct ActionAffordanceBuilder<
702    Other: ExtendableThing,
703    OtherInteractionAffordance,
704    OtherActionAffordance,
705> {
706    pub(super) interaction: InteractionAffordanceBuilder<Other, OtherInteractionAffordance>,
707    pub(super) input: Option<UncheckedDataSchemaFromOther<Other>>,
708    pub(super) output: Option<UncheckedDataSchemaFromOther<Other>>,
709    pub(super) safe: bool,
710    pub(super) idempotent: bool,
711    pub(super) synchronous: Option<bool>,
712
713    /// Action affordance extension.
714    pub other: OtherActionAffordance,
715}
716
717impl<Other, OtherInteractionAffordance, OtherActionAffordance> Default
718    for ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
719where
720    Other: ExtendableThing,
721    InteractionAffordanceBuilder<Other, OtherInteractionAffordance>: Default,
722    OtherActionAffordance: Default,
723{
724    fn default() -> Self {
725        Self {
726            interaction: Default::default(),
727            input: Default::default(),
728            output: Default::default(),
729            safe: Default::default(),
730            idempotent: Default::default(),
731            synchronous: Default::default(),
732            other: Default::default(),
733        }
734    }
735}
736
737impl<Other> ActionAffordanceBuilder<Other, Other::InteractionAffordance, Other::ActionAffordance>
738where
739    Other: ExtendableThing,
740    Other::InteractionAffordance: Extendable,
741    Other::ActionAffordance: Extendable,
742{
743    pub(crate) fn empty() -> ActionAffordanceBuilder<
744        Other,
745        <Other::InteractionAffordance as Extendable>::Empty,
746        <Other::ActionAffordance as Extendable>::Empty,
747    > {
748        ActionAffordanceBuilder {
749            interaction: InteractionAffordanceBuilder::empty(),
750            input: Default::default(),
751            output: Default::default(),
752            safe: Default::default(),
753            idempotent: Default::default(),
754            synchronous: Default::default(),
755            other: Other::ActionAffordance::empty(),
756        }
757    }
758}
759
760/// Builder for [`EventAffordance`].
761///
762/// A `EventAffordanceBuilder` behaves like an interaction affordance builder, and because of this
763/// you can customize the relative parts once the traits [`BuildableInteractionAffordance`] and
764/// [`BuildableHumanReadableInfo`] are in scope.
765///
766/// `EventAffordanceBuilder` has two level of extension:
767///
768/// - `EventAffordance` through [`ext`]/[`ext_with`]
769/// - `InteractionAffordance` through [`ext_interaction`]/[`ext_interaction_with`]
770///
771/// If the _parent_ `ThingBuilder` is extended, it is necessary to explicitly and correctly extend
772/// these two levels for `EventAffordanceBuilder`.
773///
774/// [`ext`]: Self::ext
775/// [`ext_with`]: Self::ext_with
776/// [`ext_interaction`]: Self::ext_interaction
777/// [`ext_interaction_with`]: Self::ext_interaction_with
778///
779/// # Example
780///
781/// ```
782/// # use serde::{Deserialize, Serialize};
783/// # use serde_json::json;
784/// # use wot_td::{
785/// #     builder::data_schema::SpecializableDataSchema, extend::ExtendableThing, thing::Thing,
786/// # };
787/// #
788/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
789/// struct ThingExtension {
790///     a_field: String,
791///     another_field: u32,
792/// }
793///
794/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
795/// struct InteractionExtension {
796///     ia_field: f32,
797/// }
798///
799/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
800/// struct EventExtension {
801///     event_field: u32,
802/// }
803///
804/// impl ExtendableThing for ThingExtension {
805///     type InteractionAffordance = InteractionExtension;
806///     type EventAffordance = EventExtension;
807///     /* Other types set to `()` */
808/// #   type Form = ();
809/// #   type PropertyAffordance = ();
810/// #   type ActionAffordance = ();
811/// #   type ExpectedResponse = ();
812/// #   type DataSchema = ();
813/// #   type ObjectSchema = ();
814/// #   type ArraySchema = ();
815/// }
816///
817/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
818/// struct AnotherExtension {
819///     another_field_again: Vec<String>,
820/// }
821///
822/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
823/// struct AnotherEventExtension {
824///     another_event_field: String,
825/// }
826///
827/// #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
828/// struct DataExtension {
829///     data_field: u32,
830/// }
831///
832/// impl ExtendableThing for AnotherExtension {
833///     type EventAffordance = AnotherEventExtension;
834///     /* Other types set to `()` */
835/// #   type InteractionAffordance = ();
836/// #   type PropertyAffordance = ();
837/// #   type ActionAffordance = ();
838/// #   type DataSchema = ();
839/// #   type Form = ();
840/// #   type ExpectedResponse = ();
841/// #   type ObjectSchema = ();
842/// #   type ArraySchema = ();
843/// }
844///
845/// let thing = Thing::builder("Thing name")
846///     .ext(ThingExtension {
847///         a_field: "hello world".to_string(),
848///         another_field: 42,
849///     })
850///     .ext(AnotherExtension {
851///         another_field_again: vec!["field1".to_string(), "field2".to_string()],
852///     })
853///     .finish_extend()
854///     .event("event", |b| {
855///         b.ext_interaction(InteractionExtension { ia_field: 42. })
856///             .ext_interaction(())
857///             .ext(EventExtension { event_field: 23 })
858///             .ext(AnotherEventExtension {
859///                 another_event_field: "hello".to_string(),
860///             })
861///             .subscription(|b| b.ext(()).ext(()).finish_extend().number())
862///     })
863///     .build()
864///     .unwrap();
865///
866/// assert_eq!(
867///     serde_json::to_value(thing).unwrap(),
868///     json!({
869///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
870///         "title": "Thing name",
871///         "a_field": "hello world",
872///         "another_field": 42,
873///         "another_field_again": ["field1", "field2"],
874///         "events": {
875///             "event": {
876///                 "ia_field": 42.,
877///                 "event_field": 23,
878///                 "another_event_field": "hello",
879///                 "subscription": {
880///                     "type": "number",
881///                     "readOnly": false,
882///                     "writeOnly": false,
883///                 },
884///                 "forms": [],
885///             }
886///         },
887///         "security": [],
888///         "securityDefinitions": {},
889///     })
890/// );
891/// ```
892#[derive(Default)]
893pub struct EventAffordanceBuilder<
894    Other: ExtendableThing,
895    OtherInteractionAffordance,
896    OtherEventAffordance,
897> {
898    pub(super) interaction: InteractionAffordanceBuilder<Other, OtherInteractionAffordance>,
899    pub(super) subscription: Option<UncheckedDataSchemaFromOther<Other>>,
900    pub(super) data: Option<UncheckedDataSchemaFromOther<Other>>,
901    pub(super) cancellation: Option<UncheckedDataSchemaFromOther<Other>>,
902    pub(super) data_response: Option<UncheckedDataSchemaFromOther<Other>>,
903
904    /// Event affordance extension.
905    pub other: OtherEventAffordance,
906}
907
908type EmptyEventAffordanceBuilder<Other> = EventAffordanceBuilder<
909    Other,
910    <<Other as ExtendableThing>::InteractionAffordance as Extendable>::Empty,
911    <<Other as ExtendableThing>::EventAffordance as Extendable>::Empty,
912>;
913
914impl<Other> EventAffordanceBuilder<Other, Other::InteractionAffordance, Other::EventAffordance>
915where
916    Other: ExtendableThing,
917    Other::InteractionAffordance: Extendable,
918    Other::EventAffordance: Extendable,
919{
920    pub(crate) fn empty() -> EmptyEventAffordanceBuilder<Other> {
921        EventAffordanceBuilder {
922            interaction: InteractionAffordanceBuilder::empty(),
923            subscription: Default::default(),
924            data: Default::default(),
925            cancellation: Default::default(),
926            data_response: Default::default(),
927            other: Other::EventAffordance::empty(),
928        }
929    }
930}
931
932impl<Other, OtherInteractionAffordance, OtherEventAffordance>
933    IntoUsable<UsableEventAffordanceBuilder<Other>>
934    for EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
935where
936    Other: ExtendableThing,
937    OtherInteractionAffordance: Into<Other::InteractionAffordance>,
938    OtherEventAffordance: Into<Other::EventAffordance>,
939{
940    fn into_usable(self) -> UsableEventAffordanceBuilder<Other> {
941        let Self {
942            interaction,
943            subscription,
944            data,
945            cancellation,
946            data_response,
947            other,
948        } = self;
949
950        let interaction = {
951            let InteractionAffordanceBuilder {
952                partial:
953                    PartialInteractionAffordanceBuilder {
954                        forms,
955                        uri_variables,
956                        other,
957                    },
958                info,
959            } = interaction;
960            let other = other.into();
961            let partial = PartialInteractionAffordanceBuilder {
962                forms,
963                uri_variables,
964                other,
965            };
966            InteractionAffordanceBuilder { partial, info }
967        };
968        let other = other.into();
969
970        UsableEventAffordanceBuilder {
971            interaction,
972            subscription,
973            data,
974            cancellation,
975            data_response,
976            other,
977        }
978    }
979}
980
981pub(super) type UsablePropertyAffordanceBuilder<Other> = PropertyAffordanceBuilder<
982    Other,
983    PartialDataSchema<
984        <Other as ExtendableThing>::DataSchema,
985        <Other as ExtendableThing>::ArraySchema,
986        <Other as ExtendableThing>::ObjectSchema,
987    >,
988    <Other as ExtendableThing>::InteractionAffordance,
989    <Other as ExtendableThing>::PropertyAffordance,
990>;
991pub(super) type UsableActionAffordanceBuilder<Other> = ActionAffordanceBuilder<
992    Other,
993    <Other as ExtendableThing>::InteractionAffordance,
994    <Other as ExtendableThing>::ActionAffordance,
995>;
996pub(super) type UsableEventAffordanceBuilder<Other> = EventAffordanceBuilder<
997    Other,
998    <Other as ExtendableThing>::InteractionAffordance,
999    <Other as ExtendableThing>::EventAffordance,
1000>;
1001
1002impl<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance, Status>
1003    BuildableDataSchema<Other::DataSchema, Other::ArraySchema, Other::ObjectSchema, Status>
1004    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1005where
1006    Other: ExtendableThing,
1007    CDS: BuildableDataSchema<Other::DataSchema, Other::ArraySchema, Other::ObjectSchema, Status>,
1008{
1009    #[inline]
1010    fn unit(mut self, value: impl Into<String>) -> Self {
1011        buildable_data_schema_delegate!(self.data_schema -> unit(value))
1012    }
1013
1014    #[inline]
1015    fn format(mut self, value: impl Into<String>) -> Self {
1016        buildable_data_schema_delegate!(self.data_schema -> format(value))
1017    }
1018
1019    #[inline]
1020    fn default_value(mut self, value: impl Into<Value>) -> Self {
1021        buildable_data_schema_delegate!(self.data_schema -> default_value(value))
1022    }
1023}
1024
1025impl_delegate_buildable_hr_info!(
1026    InteractionAffordanceBuilder<Other: ExtendableThing, OtherInteractionAffordance> on info,
1027    PropertyAffordanceBuilder<Other: ExtendableThing, DS, OtherInteractionAffordance, OtherPropertyAffordance> on info,
1028    ActionAffordanceBuilder<Other: ExtendableThing, OtherInteractionAffordance, OtherPropertyAffordance> on interaction,
1029    EventAffordanceBuilder<Other: ExtendableThing, Affordance, OtherPropertyAffordance> on interaction,
1030);
1031
1032impl<Other>
1033    PropertyAffordanceBuilder<
1034        Other,
1035        PartialDataSchemaBuilder<
1036            <Other::DataSchema as Extendable>::Empty,
1037            Other::ArraySchema,
1038            Other::ObjectSchema,
1039            ToExtend,
1040        >,
1041        <Other::InteractionAffordance as Extendable>::Empty,
1042        <Other::PropertyAffordance as Extendable>::Empty,
1043    >
1044where
1045    Other: ExtendableThing,
1046    Other::DataSchema: Extendable,
1047    Other::InteractionAffordance: Extendable,
1048    Other::PropertyAffordance: Extendable,
1049{
1050    pub(crate) fn empty() -> Self {
1051        Self {
1052            interaction: PartialInteractionAffordanceBuilder::empty(),
1053            info: Default::default(),
1054            data_schema: PartialDataSchemaBuilder::<Other::DataSchema, _, _, _>::empty(),
1055            observable: Default::default(),
1056            other: Other::PropertyAffordance::empty(),
1057        }
1058    }
1059}
1060
1061impl<Other: ExtendableThing, DS, OtherInteractionAffordance, OtherPropertyAffordance>
1062    PropertyAffordanceBuilder<Other, DS, OtherInteractionAffordance, OtherPropertyAffordance>
1063{
1064    /// Extends the interaction affordance, passing a closure that returns `T`.
1065    pub fn ext_interaction_with<F, T>(
1066        self,
1067        f: F,
1068    ) -> PropertyAffordanceBuilder<
1069        Other,
1070        DS,
1071        OtherInteractionAffordance::Target,
1072        OtherPropertyAffordance,
1073    >
1074    where
1075        OtherInteractionAffordance: Extend<T>,
1076        F: FnOnce() -> T,
1077    {
1078        let Self {
1079            interaction,
1080            info,
1081            data_schema,
1082            observable,
1083            other,
1084        } = self;
1085        let interaction = interaction.ext_with(f);
1086        PropertyAffordanceBuilder {
1087            interaction,
1088            info,
1089            data_schema,
1090            observable,
1091            other,
1092        }
1093    }
1094
1095    /// Extends the interaction affordance with an additional element.
1096    #[inline]
1097    pub fn ext_interaction<T>(
1098        self,
1099        t: T,
1100    ) -> PropertyAffordanceBuilder<
1101        Other,
1102        DS,
1103        OtherInteractionAffordance::Target,
1104        OtherPropertyAffordance,
1105    >
1106    where
1107        OtherInteractionAffordance: Extend<T>,
1108    {
1109        self.ext_interaction_with(|| t)
1110    }
1111
1112    /// Extends the property affordance, passing a closure that returns `T`.
1113    pub fn ext_with<F, T>(
1114        self,
1115        f: F,
1116    ) -> PropertyAffordanceBuilder<
1117        Other,
1118        DS,
1119        OtherInteractionAffordance,
1120        OtherPropertyAffordance::Target,
1121    >
1122    where
1123        OtherPropertyAffordance: Extend<T>,
1124        F: FnOnce() -> T,
1125    {
1126        let Self {
1127            interaction,
1128            info,
1129            data_schema,
1130            observable,
1131            other,
1132        } = self;
1133        let other = other.ext_with(f);
1134        PropertyAffordanceBuilder {
1135            interaction,
1136            info,
1137            data_schema,
1138            observable,
1139            other,
1140        }
1141    }
1142
1143    /// Extends the property affordance with an additional element.
1144    #[inline]
1145    pub fn ext<T>(
1146        self,
1147        t: T,
1148    ) -> PropertyAffordanceBuilder<
1149        Other,
1150        DS,
1151        OtherInteractionAffordance,
1152        OtherPropertyAffordance::Target,
1153    >
1154    where
1155        OtherPropertyAffordance: Extend<T>,
1156    {
1157        self.ext_with(|| t)
1158    }
1159}
1160
1161impl<Other, OtherInteractionAffordance, OtherPropertyAffordance, DS, AS, OS>
1162    PropertyAffordanceBuilder<
1163        Other,
1164        PartialDataSchemaBuilder<DS, AS, OS, ToExtend>,
1165        OtherInteractionAffordance,
1166        OtherPropertyAffordance,
1167    >
1168where
1169    Other: ExtendableThing,
1170{
1171    /// Extends the data schema, passing a closure that returns `T`.
1172    pub fn ext_data_schema_with<F, T>(
1173        self,
1174        f: F,
1175    ) -> PropertyAffordanceBuilder<
1176        Other,
1177        PartialDataSchemaBuilder<DS::Target, AS, OS, ToExtend>,
1178        OtherInteractionAffordance,
1179        OtherPropertyAffordance,
1180    >
1181    where
1182        F: FnOnce() -> T,
1183        DS: Extend<T>,
1184    {
1185        let Self {
1186            interaction,
1187            info,
1188            data_schema,
1189            observable,
1190            other,
1191        } = self;
1192        let data_schema = data_schema.ext_with(f);
1193        PropertyAffordanceBuilder {
1194            interaction,
1195            info,
1196            data_schema,
1197            observable,
1198            other,
1199        }
1200    }
1201
1202    /// Extends the data schema with an additional element.
1203    #[inline]
1204    pub fn ext_data_schema<T>(
1205        self,
1206        t: T,
1207    ) -> PropertyAffordanceBuilder<
1208        Other,
1209        PartialDataSchemaBuilder<DS::Target, AS, OS, ToExtend>,
1210        OtherInteractionAffordance,
1211        OtherPropertyAffordance,
1212    >
1213    where
1214        DS: Extend<T>,
1215    {
1216        self.ext_data_schema_with(|| t)
1217    }
1218
1219    /// Makes the builder unextendable and allows further customizations.
1220    pub fn finish_extend_data_schema(
1221        self,
1222    ) -> PropertyAffordanceBuilder<
1223        Other,
1224        PartialDataSchemaBuilder<DS, AS, OS, Extended>,
1225        OtherInteractionAffordance,
1226        OtherPropertyAffordance,
1227    > {
1228        let Self {
1229            interaction,
1230            info,
1231            data_schema,
1232            observable,
1233            other,
1234        } = self;
1235        let data_schema = data_schema.finish_extend();
1236        PropertyAffordanceBuilder {
1237            interaction,
1238            info,
1239            data_schema,
1240            observable,
1241            other,
1242        }
1243    }
1244}
1245
1246impl<Other, OtherInteractionAffordance, OtherPropertyAffordance, DS, AS, OS>
1247    PropertyAffordanceBuilder<
1248        Other,
1249        DataSchemaBuilder<DS, AS, OS, ToExtend>,
1250        OtherInteractionAffordance,
1251        OtherPropertyAffordance,
1252    >
1253where
1254    Other: ExtendableThing,
1255{
1256    /// Extends the data schema, passing a closure that returns `T`.
1257    pub fn ext_data_schema_with<F, T>(
1258        self,
1259        f: F,
1260    ) -> PropertyAffordanceBuilder<
1261        Other,
1262        DataSchemaBuilder<DS::Target, AS, OS, ToExtend>,
1263        OtherInteractionAffordance,
1264        OtherPropertyAffordance,
1265    >
1266    where
1267        F: FnOnce() -> T,
1268        DS: Extend<T>,
1269    {
1270        let Self {
1271            interaction,
1272            info,
1273            data_schema,
1274            observable,
1275            other,
1276        } = self;
1277        let data_schema = data_schema.ext_with(f);
1278        PropertyAffordanceBuilder {
1279            interaction,
1280            info,
1281            data_schema,
1282            observable,
1283            other,
1284        }
1285    }
1286
1287    /// Extends the data schema with an additional element.
1288    #[inline]
1289    pub fn ext_data_schema<T>(
1290        self,
1291        t: T,
1292    ) -> PropertyAffordanceBuilder<
1293        Other,
1294        DataSchemaBuilder<DS::Target, AS, OS, ToExtend>,
1295        OtherInteractionAffordance,
1296        OtherPropertyAffordance,
1297    >
1298    where
1299        DS: Extend<T>,
1300    {
1301        self.ext_data_schema_with(|| t)
1302    }
1303
1304    /// Makes the builder unextendable and allows further customizations.
1305    pub fn finish_extend_data_schema(
1306        self,
1307    ) -> PropertyAffordanceBuilder<
1308        Other,
1309        DataSchemaBuilder<DS, AS, OS, Extended>,
1310        OtherInteractionAffordance,
1311        OtherPropertyAffordance,
1312    > {
1313        let Self {
1314            interaction,
1315            info,
1316            data_schema,
1317            observable,
1318            other,
1319        } = self;
1320        let data_schema = data_schema.finish_extend();
1321        PropertyAffordanceBuilder {
1322            interaction,
1323            info,
1324            data_schema,
1325            observable,
1326            other,
1327        }
1328    }
1329}
1330
1331impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1332    IntoUsable<UsablePropertyAffordanceBuilder<Other>>
1333    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1334where
1335    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1336    CDS: Into<PartialDataSchema<DS, AS, OS>>,
1337    PartialInteractionAffordanceBuilder<Other, OtherInteractionAffordance>:
1338        IntoUsable<PartialInteractionAffordanceBuilder<Other, Other::InteractionAffordance>>,
1339    OtherPropertyAffordance: Into<Other::PropertyAffordance>,
1340{
1341    fn into_usable(self) -> UsablePropertyAffordanceBuilder<Other> {
1342        let Self {
1343            interaction,
1344            info,
1345            data_schema,
1346            observable,
1347            other,
1348        } = self;
1349
1350        let interaction = interaction.into_usable();
1351        let data_schema = data_schema.into();
1352        let other = other.into();
1353        PropertyAffordanceBuilder {
1354            interaction,
1355            info,
1356            data_schema,
1357            observable,
1358            other,
1359        }
1360    }
1361}
1362
1363impl<Other: ExtendableThing, OtherInteractionAffordance, OtherActionAffordance>
1364    ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
1365{
1366    /// Extends the interaction affordance, passing a closure that returns `T`.
1367    pub fn ext_interaction_with<F, T>(
1368        self,
1369        f: F,
1370    ) -> ActionAffordanceBuilder<Other, OtherInteractionAffordance::Target, OtherActionAffordance>
1371    where
1372        OtherInteractionAffordance: Extend<T>,
1373        F: FnOnce() -> T,
1374    {
1375        let Self {
1376            interaction,
1377            input,
1378            output,
1379            safe,
1380            idempotent,
1381            synchronous,
1382            other,
1383        } = self;
1384        let interaction = interaction.ext_with(f);
1385        ActionAffordanceBuilder {
1386            interaction,
1387            input,
1388            output,
1389            safe,
1390            idempotent,
1391            synchronous,
1392            other,
1393        }
1394    }
1395
1396    /// Extends the interaction affordance with an additional element.
1397    #[inline]
1398    pub fn ext_interaction<T>(
1399        self,
1400        t: T,
1401    ) -> ActionAffordanceBuilder<Other, OtherInteractionAffordance::Target, OtherActionAffordance>
1402    where
1403        OtherInteractionAffordance: Extend<T>,
1404    {
1405        self.ext_interaction_with(|| t)
1406    }
1407
1408    /// Extends the action affordance, passing a closure that returns `T`.
1409    pub fn ext_with<F, T>(
1410        self,
1411        f: F,
1412    ) -> ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance::Target>
1413    where
1414        OtherActionAffordance: Extend<T>,
1415        F: FnOnce() -> T,
1416    {
1417        let Self {
1418            interaction,
1419            input,
1420            output,
1421            safe,
1422            idempotent,
1423            synchronous,
1424            other,
1425        } = self;
1426        let other = other.ext_with(f);
1427        ActionAffordanceBuilder {
1428            interaction,
1429            input,
1430            output,
1431            safe,
1432            idempotent,
1433            synchronous,
1434            other,
1435        }
1436    }
1437
1438    /// Extends the action affordance with an additional element.
1439    #[inline]
1440    pub fn ext<T>(
1441        self,
1442        t: T,
1443    ) -> ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance::Target>
1444    where
1445        OtherActionAffordance: Extend<T>,
1446    {
1447        self.ext_with(|| t)
1448    }
1449}
1450
1451impl<Other: ExtendableThing, OtherInteractionAffordance, OtherEventAffordance>
1452    EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
1453{
1454    /// Extends the interaction affordance, passing a closure that returns `T`.
1455    pub fn ext_interaction_with<F, T>(
1456        self,
1457        f: F,
1458    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance::Target, OtherEventAffordance>
1459    where
1460        OtherInteractionAffordance: Extend<T>,
1461        F: FnOnce() -> T,
1462    {
1463        let Self {
1464            interaction,
1465            subscription,
1466            data,
1467            cancellation,
1468            data_response,
1469            other,
1470        } = self;
1471        let interaction = interaction.ext_with(f);
1472        EventAffordanceBuilder {
1473            interaction,
1474            subscription,
1475            data,
1476            cancellation,
1477            data_response,
1478            other,
1479        }
1480    }
1481
1482    /// Extends the interaction affordance with an additional element.
1483    #[inline]
1484    pub fn ext_interaction<T>(
1485        self,
1486        t: T,
1487    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance::Target, OtherEventAffordance>
1488    where
1489        OtherInteractionAffordance: Extend<T>,
1490    {
1491        self.ext_interaction_with(|| t)
1492    }
1493
1494    /// Extends the event affordance, passing a closure that returns `T`.
1495    pub fn ext_with<F, T>(
1496        self,
1497        f: F,
1498    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance::Target>
1499    where
1500        OtherEventAffordance: Extend<T>,
1501        F: FnOnce() -> T,
1502    {
1503        let Self {
1504            interaction,
1505            subscription,
1506            data,
1507            cancellation,
1508            data_response,
1509            other,
1510        } = self;
1511        let other = other.ext_with(f);
1512        EventAffordanceBuilder {
1513            interaction,
1514            subscription,
1515            data,
1516            cancellation,
1517            data_response,
1518            other,
1519        }
1520    }
1521
1522    /// Extends the event affordance with an additional element.
1523    #[inline]
1524    pub fn ext<T>(
1525        self,
1526        t: T,
1527    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance::Target>
1528    where
1529        OtherEventAffordance: Extend<T>,
1530    {
1531        self.ext_with(|| t)
1532    }
1533}
1534
1535impl<Other: ExtendableThing, DataSchema, OtherInteractionAffordance, OtherPropertyAffordance>
1536    PropertyAffordanceBuilder<
1537        Other,
1538        DataSchema,
1539        OtherInteractionAffordance,
1540        OtherPropertyAffordance,
1541    >
1542{
1543    /// Sets the value of the `observable` field.
1544    pub fn observable(mut self, value: bool) -> Self {
1545        self.observable = Some(value);
1546        self
1547    }
1548}
1549
1550macro_rules! impl_property_affordance_builder_delegator {
1551    ($($name:ident $(< $($generic_def:ident),+ >)? $( ( $($arg:ident : $arg_ty:ty),+ ) )? $(where $($generic:ident : $bound:path),+)? => $ty:ty),+ $(,)?) => {
1552        $(
1553            fn $name $(<$($generic_def),+>)? (self $(, $($arg: $arg_ty),+)?) -> $ty
1554            $(
1555                where
1556                    $($generic: $bound),+
1557            )?
1558            {
1559                let Self {
1560                    interaction,
1561                    info,
1562                    data_schema,
1563                    observable,
1564                    other,
1565                } = self;
1566
1567                let data_schema = data_schema.$name($($($arg),+)?);
1568                PropertyAffordanceBuilder {
1569                    interaction,
1570                    info,
1571                    data_schema,
1572                    observable,
1573                    other,
1574                }
1575            }
1576        )+
1577    };
1578}
1579
1580impl<Other, DataSchema, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1581    SpecializableDataSchema<DS, AS, OS>
1582    for PropertyAffordanceBuilder<
1583        Other,
1584        DataSchema,
1585        OtherInteractionAffordance,
1586        OtherPropertyAffordance,
1587    >
1588where
1589    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1590    DataSchema: SpecializableDataSchema<DS, AS, OS>,
1591{
1592    type Stateless = PropertyAffordanceBuilder<
1593        Other,
1594        DataSchema::Stateless,
1595        OtherInteractionAffordance,
1596        OtherPropertyAffordance,
1597    >;
1598    type Tuple = PropertyAffordanceBuilder<
1599        Other,
1600        DataSchema::Tuple,
1601        OtherInteractionAffordance,
1602        OtherPropertyAffordance,
1603    >;
1604    type Vec = PropertyAffordanceBuilder<
1605        Other,
1606        DataSchema::Vec,
1607        OtherInteractionAffordance,
1608        OtherPropertyAffordance,
1609    >;
1610    type Number = PropertyAffordanceBuilder<
1611        Other,
1612        DataSchema::Number,
1613        OtherInteractionAffordance,
1614        OtherPropertyAffordance,
1615    >;
1616    type Integer = PropertyAffordanceBuilder<
1617        Other,
1618        DataSchema::Integer,
1619        OtherInteractionAffordance,
1620        OtherPropertyAffordance,
1621    >;
1622    type Object = PropertyAffordanceBuilder<
1623        Other,
1624        DataSchema::Object,
1625        OtherInteractionAffordance,
1626        OtherPropertyAffordance,
1627    >;
1628    type String = PropertyAffordanceBuilder<
1629        Other,
1630        DataSchema::String,
1631        OtherInteractionAffordance,
1632        OtherPropertyAffordance,
1633    >;
1634    type Constant = PropertyAffordanceBuilder<
1635        Other,
1636        DataSchema::Constant,
1637        OtherInteractionAffordance,
1638        OtherPropertyAffordance,
1639    >;
1640
1641    impl_property_affordance_builder_delegator!(
1642        tuple where AS: Default => Self::Tuple,
1643        tuple_ext<F>(f: F) where F: FnOnce(AS::Empty) -> AS, AS: Extendable => Self::Tuple,
1644        vec where AS: Default => Self::Vec,
1645        vec_ext<F>(f: F) where F: FnOnce(AS::Empty) -> AS, AS: Extendable => Self::Vec,
1646        bool => Self::Stateless,
1647        number => Self::Number,
1648        integer => Self::Integer,
1649        object where OS: Default => Self::Object,
1650        object_ext<F>(f: F) where F: FnOnce(OS::Empty) -> OS, OS: Extendable => Self::Object,
1651        string => Self::String,
1652        null => Self::Stateless,
1653        constant(value: impl Into<Value>) => Self::Constant,
1654    );
1655}
1656
1657impl<Other, DataSchema, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1658    EnumerableDataSchema<DS, AS, OS, Extended>
1659    for PropertyAffordanceBuilder<
1660        Other,
1661        DataSchema,
1662        OtherInteractionAffordance,
1663        OtherPropertyAffordance,
1664    >
1665where
1666    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1667    DataSchema: EnumerableDataSchema<DS, AS, OS, Extended>,
1668{
1669    type Target = PropertyAffordanceBuilder<
1670        Other,
1671        DataSchema::Target,
1672        OtherInteractionAffordance,
1673        OtherPropertyAffordance,
1674    >;
1675
1676    fn enumeration(self, value: impl Into<Value>) -> Self::Target {
1677        let Self {
1678            interaction,
1679            info,
1680            data_schema,
1681            observable,
1682            other,
1683        } = self;
1684
1685        let data_schema = data_schema.enumeration(value);
1686
1687        PropertyAffordanceBuilder {
1688            interaction,
1689            info,
1690            data_schema,
1691            observable,
1692            other,
1693        }
1694    }
1695}
1696
1697impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1698    UnionDataSchema<DS, AS, OS>
1699    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1700where
1701    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1702    CDS: UnionDataSchema<DS, AS, OS>,
1703{
1704    type Target = PropertyAffordanceBuilder<
1705        Other,
1706        CDS::Target,
1707        OtherInteractionAffordance,
1708        OtherPropertyAffordance,
1709    >;
1710
1711    fn one_of<F, T>(self, f: F) -> Self::Target
1712    where
1713        F: FnOnce(
1714            DataSchemaBuilder<
1715                <DS as Extendable>::Empty,
1716                Other::ArraySchema,
1717                Other::ObjectSchema,
1718                ToExtend,
1719            >,
1720        ) -> T,
1721        DS: Extendable,
1722        T: Into<UncheckedDataSchemaFromOther<Other>>,
1723    {
1724        let Self {
1725            interaction,
1726            info,
1727            data_schema,
1728            observable,
1729            other,
1730        } = self;
1731
1732        let data_schema = data_schema.one_of(f);
1733        PropertyAffordanceBuilder {
1734            interaction,
1735            info,
1736            data_schema,
1737            observable,
1738            other,
1739        }
1740    }
1741}
1742
1743impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1744    ReadableWriteableDataSchema<DS, AS, OS, Extended>
1745    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1746where
1747    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1748    CDS: ReadableWriteableDataSchema<DS, AS, OS, Extended>,
1749{
1750    type ReadOnly = PropertyAffordanceBuilder<
1751        Other,
1752        CDS::ReadOnly,
1753        OtherInteractionAffordance,
1754        OtherPropertyAffordance,
1755    >;
1756    type WriteOnly = PropertyAffordanceBuilder<
1757        Other,
1758        CDS::WriteOnly,
1759        OtherInteractionAffordance,
1760        OtherPropertyAffordance,
1761    >;
1762
1763    fn read_only(self) -> Self::ReadOnly {
1764        let Self {
1765            interaction,
1766            info,
1767            data_schema,
1768            observable,
1769            other,
1770        } = self;
1771
1772        let data_schema = data_schema.read_only();
1773        PropertyAffordanceBuilder {
1774            interaction,
1775            info,
1776            data_schema,
1777            observable,
1778            other,
1779        }
1780    }
1781
1782    fn write_only(self) -> Self::WriteOnly {
1783        let Self {
1784            interaction,
1785            info,
1786            data_schema,
1787            observable,
1788            other,
1789        } = self;
1790
1791        let data_schema = data_schema.write_only();
1792        PropertyAffordanceBuilder {
1793            interaction,
1794            info,
1795            data_schema,
1796            observable,
1797            other,
1798        }
1799    }
1800}
1801
1802impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1803    TupleDataSchemaBuilderLike<DS, AS, OS>
1804    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1805where
1806    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1807    CDS: TupleDataSchemaBuilderLike<DS, AS, OS>,
1808{
1809    impl_inner_delegate_schema_builder_like_tuple!(data_schema);
1810}
1811
1812impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1813    VecDataSchemaBuilderLike<DS, AS, OS>
1814    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1815where
1816    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1817    CDS: VecDataSchemaBuilderLike<DS, AS, OS>,
1818{
1819    impl_inner_delegate_schema_builder_like_vec!(data_schema);
1820}
1821
1822impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1823    NumberDataSchemaBuilderLike<DS, AS, OS>
1824    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1825where
1826    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1827    CDS: NumberDataSchemaBuilderLike<DS, AS, OS>,
1828{
1829    impl_inner_delegate_schema_builder_like_number!(data_schema);
1830}
1831
1832impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1833    IntegerDataSchemaBuilderLike<DS, AS, OS>
1834    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1835where
1836    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1837    CDS: IntegerDataSchemaBuilderLike<DS, AS, OS>,
1838{
1839    impl_inner_delegate_schema_builder_like_integer!(data_schema);
1840}
1841
1842impl<Other, CDS, DS, AS, OS, OtherInteractionAffordance, OtherPropertyAffordance>
1843    ObjectDataSchemaBuilderLike<DS, AS, OS>
1844    for PropertyAffordanceBuilder<Other, CDS, OtherInteractionAffordance, OtherPropertyAffordance>
1845where
1846    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
1847    CDS: ObjectDataSchemaBuilderLike<DS, AS, OS>,
1848{
1849    impl_inner_delegate_schema_builder_like_object!(data_schema);
1850}
1851
1852impl<Other: ExtendableThing, OtherInteractionAffordance, OtherActionAffordance>
1853    ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
1854{
1855    /// Adds a new _input_ schema.
1856    ///
1857    /// It takes a function that accepts a `DataSchema` builder and must return a
1858    /// type convertible into a `DataSchema`.
1859    ///
1860    /// # Example
1861    /// ```
1862    /// # use serde_json::json;
1863    /// # use wot_td::{builder::data_schema::SpecializableDataSchema, thing::Thing};
1864    /// #
1865    /// let thing = Thing::builder("Thing name")
1866    ///     .finish_extend()
1867    ///     .action("action", |b| b.input(|b| b.finish_extend().number()))
1868    ///     .build()
1869    ///     .unwrap();
1870    ///
1871    /// assert_eq!(
1872    ///     serde_json::to_value(thing).unwrap(),
1873    ///     json!({
1874    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
1875    ///         "title": "Thing name",
1876    ///         "actions": {
1877    ///             "action": {
1878    ///                 "input": {
1879    ///                     "type": "number",
1880    ///                     "readOnly": false,
1881    ///                     "writeOnly": false,
1882    ///                 },
1883    ///                 "idempotent": false,
1884    ///                 "safe": false,
1885    ///                 "forms": [],
1886    ///             }
1887    ///         },
1888    ///         "security": [],
1889    ///         "securityDefinitions": {},
1890    ///     })
1891    /// );
1892    /// ```
1893    pub fn input<F, T>(
1894        self,
1895        f: F,
1896    ) -> ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
1897    where
1898        F: FnOnce(
1899            DataSchemaBuilder<
1900                <Other::DataSchema as Extendable>::Empty,
1901                Other::ArraySchema,
1902                Other::ObjectSchema,
1903                ToExtend,
1904            >,
1905        ) -> T,
1906        T: Into<UncheckedDataSchemaFromOther<Other>>,
1907        Other::DataSchema: Extendable,
1908    {
1909        let Self {
1910            interaction,
1911            input: _,
1912            output,
1913            safe,
1914            idempotent,
1915            synchronous,
1916            other,
1917        } = self;
1918        let input = Some(f(DataSchemaBuilder::<Other::DataSchema, _, _, _>::empty()).into());
1919
1920        ActionAffordanceBuilder {
1921            interaction,
1922            input,
1923            output,
1924            safe,
1925            idempotent,
1926            synchronous,
1927            other,
1928        }
1929    }
1930}
1931
1932impl<Other: ExtendableThing, OtherInteractionAffordance, OtherActionAffordance>
1933    ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
1934{
1935    /// Adds a new _output_ schema.
1936    ///
1937    /// It takes a function that accepts a `DataSchema` builder and must return a
1938    /// type convertible into a `DataSchema`.
1939    ///
1940    /// # Example
1941    /// ```
1942    /// # use serde_json::json;
1943    /// # use wot_td::{builder::data_schema::SpecializableDataSchema, thing::Thing};
1944    /// #
1945    /// let thing = Thing::builder("Thing name")
1946    ///     .finish_extend()
1947    ///     .action("action", |b| b.output(|b| b.finish_extend().string()))
1948    ///     .build()
1949    ///     .unwrap();
1950    ///
1951    /// assert_eq!(
1952    ///     serde_json::to_value(thing).unwrap(),
1953    ///     json!({
1954    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
1955    ///         "title": "Thing name",
1956    ///         "actions": {
1957    ///             "action": {
1958    ///                 "output": {
1959    ///                     "type": "string",
1960    ///                     "readOnly": false,
1961    ///                     "writeOnly": false,
1962    ///                 },
1963    ///                 "idempotent": false,
1964    ///                 "safe": false,
1965    ///                 "forms": [],
1966    ///             }
1967    ///         },
1968    ///         "security": [],
1969    ///         "securityDefinitions": {},
1970    ///     })
1971    /// );
1972    /// ```
1973    pub fn output<F, T>(
1974        self,
1975        f: F,
1976    ) -> ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
1977    where
1978        F: FnOnce(
1979            DataSchemaBuilder<
1980                <Other::DataSchema as Extendable>::Empty,
1981                Other::ArraySchema,
1982                Other::ObjectSchema,
1983                ToExtend,
1984            >,
1985        ) -> T,
1986        T: Into<UncheckedDataSchemaFromOther<Other>>,
1987        Other::DataSchema: Extendable,
1988    {
1989        let Self {
1990            interaction,
1991            input,
1992            output: _,
1993            safe,
1994            idempotent,
1995            synchronous,
1996            other,
1997        } = self;
1998        let output = Some(f(DataSchemaBuilder::<Other::DataSchema, _, _, _>::empty()).into());
1999
2000        ActionAffordanceBuilder {
2001            interaction,
2002            input,
2003            output,
2004            safe,
2005            idempotent,
2006            synchronous,
2007            other,
2008        }
2009    }
2010}
2011
2012impl<Other: ExtendableThing, OtherInteractionAffordance, OtherActionAffordance>
2013    ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
2014{
2015    /// Sets the value of the `safe` field to `true`.
2016    pub fn safe(mut self) -> Self {
2017        self.safe = true;
2018        self
2019    }
2020
2021    /// Sets the value of the `idempotent` field to `true`.
2022    pub fn idempotent(mut self) -> Self {
2023        self.idempotent = true;
2024        self
2025    }
2026
2027    /// Sets the value of the `synchronous` field.
2028    pub fn synchronous(mut self, value: bool) -> Self {
2029        self.synchronous = Some(value);
2030        self
2031    }
2032}
2033
2034impl<Other: ExtendableThing, OtherInteractionAffordance, OtherEventAffordance>
2035    EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2036{
2037    /// Adds a new _subscription_ schema.
2038    ///
2039    /// It takes a function that accepts a `DataSchema` builder and must return a
2040    /// type convertible into a `DataSchema`.
2041    ///
2042    /// # Example
2043    /// ```
2044    /// # use serde_json::json;
2045    /// # use wot_td::{builder::data_schema::SpecializableDataSchema, thing::Thing};
2046    /// #
2047    /// let thing = Thing::builder("Thing name")
2048    ///     .finish_extend()
2049    ///     .event("event", |b| b.subscription(|b| b.finish_extend().number()))
2050    ///     .build()
2051    ///     .unwrap();
2052    ///
2053    /// assert_eq!(
2054    ///     serde_json::to_value(thing).unwrap(),
2055    ///     json!({
2056    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
2057    ///         "title": "Thing name",
2058    ///         "events": {
2059    ///             "event": {
2060    ///                 "subscription": {
2061    ///                     "type": "number",
2062    ///                     "readOnly": false,
2063    ///                     "writeOnly": false,
2064    ///                 },
2065    ///                 "forms": [],
2066    ///             }
2067    ///         },
2068    ///         "security": [],
2069    ///         "securityDefinitions": {},
2070    ///     })
2071    /// );
2072    /// ```
2073    pub fn subscription<F, T>(
2074        self,
2075        f: F,
2076    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2077    where
2078        F: FnOnce(
2079            DataSchemaBuilder<
2080                <Other::DataSchema as Extendable>::Empty,
2081                Other::ArraySchema,
2082                Other::ObjectSchema,
2083                ToExtend,
2084            >,
2085        ) -> T,
2086        T: Into<UncheckedDataSchemaFromOther<Other>>,
2087        Other::DataSchema: Extendable,
2088    {
2089        let Self {
2090            interaction,
2091            subscription: _,
2092            data,
2093            cancellation,
2094            data_response,
2095            other,
2096        } = self;
2097        let subscription = Some(f(DataSchemaBuilder::<Other::DataSchema, _, _, _>::empty()).into());
2098
2099        EventAffordanceBuilder {
2100            interaction,
2101            subscription,
2102            data,
2103            cancellation,
2104            data_response,
2105            other,
2106        }
2107    }
2108}
2109
2110impl<Other: ExtendableThing, OtherInteractionAffordance, OtherEventAffordance>
2111    EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2112{
2113    /// Adds a new _data_ schema.
2114    ///
2115    /// It takes a function that accepts a `DataSchema` builder and must return a
2116    /// type convertible into a `DataSchema`.
2117    ///
2118    /// # Example
2119    /// ```
2120    /// # use serde_json::json;
2121    /// # use wot_td::{builder::data_schema::SpecializableDataSchema, thing::Thing};
2122    /// #
2123    /// let thing = Thing::builder("Thing name")
2124    ///     .finish_extend()
2125    ///     .event("event", |b| b.data(|b| b.finish_extend().number()))
2126    ///     .build()
2127    ///     .unwrap();
2128    ///
2129    /// assert_eq!(
2130    ///     serde_json::to_value(thing).unwrap(),
2131    ///     json!({
2132    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
2133    ///         "title": "Thing name",
2134    ///         "events": {
2135    ///             "event": {
2136    ///                 "data": {
2137    ///                     "type": "number",
2138    ///                     "readOnly": false,
2139    ///                     "writeOnly": false,
2140    ///                 },
2141    ///                 "forms": [],
2142    ///             }
2143    ///         },
2144    ///         "security": [],
2145    ///         "securityDefinitions": {},
2146    ///     })
2147    /// );
2148    /// ```
2149    pub fn data<F, T>(
2150        self,
2151        f: F,
2152    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2153    where
2154        F: FnOnce(
2155            DataSchemaBuilder<
2156                <Other::DataSchema as Extendable>::Empty,
2157                Other::ArraySchema,
2158                Other::ObjectSchema,
2159                ToExtend,
2160            >,
2161        ) -> T,
2162        T: Into<UncheckedDataSchemaFromOther<Other>>,
2163        Other::DataSchema: Extendable,
2164    {
2165        let Self {
2166            interaction,
2167            subscription,
2168            data: _,
2169            cancellation,
2170            data_response,
2171            other,
2172        } = self;
2173        let data = Some(f(DataSchemaBuilder::<Other::DataSchema, _, _, _>::empty()).into());
2174
2175        EventAffordanceBuilder {
2176            interaction,
2177            subscription,
2178            data,
2179            cancellation,
2180            data_response,
2181            other,
2182        }
2183    }
2184}
2185
2186impl<Other: ExtendableThing, OtherInteractionAffordance, OtherEventAffordance>
2187    EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2188{
2189    /// Adds a new _cancellation_ schema.
2190    ///
2191    /// It takes a function that accepts a `DataSchema` builder and must return a
2192    /// type convertible into a `DataSchema`.
2193    ///
2194    /// # Example
2195    /// ```
2196    /// # use serde_json::json;
2197    /// # use wot_td::{builder::data_schema::SpecializableDataSchema, thing::Thing};
2198    /// #
2199    /// let thing = Thing::builder("Thing name")
2200    ///     .finish_extend()
2201    ///     .event("event", |b| b.cancellation(|b| b.finish_extend().number()))
2202    ///     .build()
2203    ///     .unwrap();
2204    ///
2205    /// assert_eq!(
2206    ///     serde_json::to_value(thing).unwrap(),
2207    ///     json!({
2208    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
2209    ///         "title": "Thing name",
2210    ///         "events": {
2211    ///             "event": {
2212    ///                 "cancellation": {
2213    ///                     "type": "number",
2214    ///                     "readOnly": false,
2215    ///                     "writeOnly": false,
2216    ///                 },
2217    ///                 "forms": [],
2218    ///             }
2219    ///         },
2220    ///         "security": [],
2221    ///         "securityDefinitions": {},
2222    ///     })
2223    /// );
2224    /// ```
2225    pub fn cancellation<F, T>(
2226        self,
2227        f: F,
2228    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2229    where
2230        F: FnOnce(
2231            DataSchemaBuilder<
2232                <Other::DataSchema as Extendable>::Empty,
2233                Other::ArraySchema,
2234                Other::ObjectSchema,
2235                ToExtend,
2236            >,
2237        ) -> T,
2238        T: Into<UncheckedDataSchemaFromOther<Other>>,
2239        Other::DataSchema: Extendable,
2240    {
2241        let Self {
2242            interaction,
2243            subscription,
2244            data,
2245            cancellation: _,
2246            data_response,
2247            other,
2248        } = self;
2249        let cancellation = Some(f(DataSchemaBuilder::<Other::DataSchema, _, _, _>::empty()).into());
2250
2251        EventAffordanceBuilder {
2252            interaction,
2253            subscription,
2254            data,
2255            cancellation,
2256            data_response,
2257            other,
2258        }
2259    }
2260}
2261
2262impl<Other: ExtendableThing, OtherInteractionAffordance, OtherEventAffordance>
2263    EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2264{
2265    /// Adds a new _data response_ schema.
2266    ///
2267    /// It takes a function that accepts a `DataSchema` builder and must return a
2268    /// type convertible into a `DataSchema`.
2269    ///
2270    /// # Example
2271    /// ```
2272    /// # use serde_json::json;
2273    /// # use wot_td::{builder::data_schema::SpecializableDataSchema, thing::Thing};
2274    /// #
2275    /// let thing = Thing::builder("Thing name")
2276    ///     .finish_extend()
2277    ///     .event("event", |b| b.data_response(|b| b.finish_extend().number()))
2278    ///     .build()
2279    ///     .unwrap();
2280    ///
2281    /// assert_eq!(
2282    ///     serde_json::to_value(thing).unwrap(),
2283    ///     json!({
2284    ///         "@context": "https://www.w3.org/2022/wot/td/v1.1",
2285    ///         "title": "Thing name",
2286    ///         "events": {
2287    ///             "event": {
2288    ///                 "data_response": {
2289    ///                     "type": "number",
2290    ///                     "readOnly": false,
2291    ///                     "writeOnly": false,
2292    ///                 },
2293    ///                 "forms": [],
2294    ///             }
2295    ///         },
2296    ///         "security": [],
2297    ///         "securityDefinitions": {},
2298    ///     })
2299    /// );
2300    /// ```
2301    pub fn data_response<F, T>(
2302        self,
2303        f: F,
2304    ) -> EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2305    where
2306        F: FnOnce(
2307            DataSchemaBuilder<
2308                <Other::DataSchema as Extendable>::Empty,
2309                Other::ArraySchema,
2310                Other::ObjectSchema,
2311                ToExtend,
2312            >,
2313        ) -> T,
2314        T: Into<UncheckedDataSchemaFromOther<Other>>,
2315        Other::DataSchema: Extendable,
2316    {
2317        let Self {
2318            interaction,
2319            subscription,
2320            data,
2321            cancellation,
2322            data_response: _,
2323            other,
2324        } = self;
2325        let data_response =
2326            Some(f(DataSchemaBuilder::<Other::DataSchema, _, _, _>::empty()).into());
2327
2328        EventAffordanceBuilder {
2329            interaction,
2330            subscription,
2331            data,
2332            cancellation,
2333            data_response,
2334            other,
2335        }
2336    }
2337}
2338
2339impl<Other, OtherInteractionAffordance>
2340    From<InteractionAffordanceBuilder<Other, OtherInteractionAffordance>>
2341    for UncheckedInteractionAffordance<Other>
2342where
2343    Other: ExtendableThing,
2344    OtherInteractionAffordance: Into<Other::InteractionAffordance>,
2345{
2346    fn from(builder: InteractionAffordanceBuilder<Other, OtherInteractionAffordance>) -> Self {
2347        let InteractionAffordanceBuilder {
2348            info:
2349                HumanReadableInfo {
2350                    attype,
2351                    title,
2352                    titles,
2353                    description,
2354                    descriptions,
2355                },
2356            partial:
2357                PartialInteractionAffordanceBuilder {
2358                    forms,
2359                    uri_variables,
2360                    other,
2361                },
2362        } = builder;
2363
2364        let forms = forms.into_iter().map(Form::from).collect();
2365        let uri_variables = uri_variables.is_empty().not().then_some(uri_variables);
2366        let other = other.into();
2367
2368        Self {
2369            attype,
2370            title,
2371            titles,
2372            description,
2373            descriptions,
2374            forms,
2375            uri_variables,
2376            other,
2377        }
2378    }
2379}
2380
2381impl<Other: ExtendableThing, OtherInteractionAffordance, OtherActionAffordance>
2382    IntoUsable<UsableActionAffordanceBuilder<Other>>
2383    for ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
2384where
2385    OtherInteractionAffordance: Into<Other::InteractionAffordance>,
2386    OtherActionAffordance: Into<Other::ActionAffordance>,
2387{
2388    fn into_usable(self) -> UsableActionAffordanceBuilder<Other> {
2389        let ActionAffordanceBuilder {
2390            interaction,
2391            input,
2392            output,
2393            safe,
2394            idempotent,
2395            synchronous,
2396            other,
2397        } = self;
2398
2399        let interaction = {
2400            let InteractionAffordanceBuilder {
2401                partial:
2402                    PartialInteractionAffordanceBuilder {
2403                        forms,
2404                        uri_variables,
2405                        other,
2406                    },
2407                info,
2408            } = interaction;
2409            let other = other.into();
2410            let partial = PartialInteractionAffordanceBuilder {
2411                forms,
2412                uri_variables,
2413                other,
2414            };
2415            InteractionAffordanceBuilder { partial, info }
2416        };
2417
2418        let other = other.into();
2419
2420        UsableActionAffordanceBuilder {
2421            interaction,
2422            input,
2423            output,
2424            safe,
2425            idempotent,
2426            synchronous,
2427            other,
2428        }
2429    }
2430}
2431
2432pub(super) trait CheckableInteractionAffordanceBuilder {
2433    fn check<F>(
2434        &self,
2435        security_definitions: &HashMap<String, SecurityScheme>,
2436        affordance_type: AffordanceType,
2437        is_allowed_op: F,
2438    ) -> Result<(), Error>
2439    where
2440        F: Fn(FormOperation) -> bool;
2441}
2442
2443impl<Other: ExtendableThing> CheckableInteractionAffordanceBuilder
2444    for PartialInteractionAffordanceBuilder<Other, Other::InteractionAffordance>
2445{
2446    fn check<F>(
2447        &self,
2448        security_definitions: &HashMap<String, SecurityScheme>,
2449        affordance_type: AffordanceType,
2450        is_allowed_op: F,
2451    ) -> Result<(), Error>
2452    where
2453        F: Fn(FormOperation) -> bool,
2454    {
2455        check_form_builders(
2456            &self.forms,
2457            security_definitions,
2458            affordance_type,
2459            is_allowed_op,
2460        )?;
2461        if uri_variables_contains_arrays_objects::<Other>(&self.uri_variables) {
2462            return Err(Error::InvalidUriVariables);
2463        }
2464
2465        Ok(())
2466    }
2467}
2468
2469impl<Other: ExtendableThing> CheckableInteractionAffordanceBuilder
2470    for InteractionAffordanceBuilder<Other, Other::InteractionAffordance>
2471{
2472    fn check<F>(
2473        &self,
2474        security_definitions: &HashMap<String, SecurityScheme>,
2475        affordance_type: AffordanceType,
2476        is_allowed_op: F,
2477    ) -> Result<(), Error>
2478    where
2479        F: Fn(FormOperation) -> bool,
2480    {
2481        check_form_builders(
2482            &self.partial.forms,
2483            security_definitions,
2484            affordance_type,
2485            is_allowed_op,
2486        )?;
2487        if uri_variables_contains_arrays_objects::<Other>(&self.partial.uri_variables) {
2488            return Err(Error::InvalidUriVariables);
2489        }
2490
2491        Ok(())
2492    }
2493}
2494
2495pub(super) fn check_form_builders<Other, F>(
2496    forms: &[FormBuilder<Other, String, Other::Form>],
2497    security_definitions: &HashMap<String, SecurityScheme>,
2498    affordance_type: AffordanceType,
2499    is_allowed_op: F,
2500) -> Result<(), Error>
2501where
2502    Other: ExtendableThing,
2503    F: Fn(FormOperation) -> bool,
2504{
2505    for form in forms {
2506        if let DefaultedFormOperations::Custom(ops) = &form.op {
2507            let invalid_op = ops.iter().copied().find(|&op| is_allowed_op(op).not());
2508            if let Some(operation) = invalid_op {
2509                return Err(Error::InvalidOpInForm {
2510                    context: affordance_type.into(),
2511                    operation,
2512                });
2513            }
2514        }
2515
2516        form.security
2517            .as_ref()
2518            .map(|securities| {
2519                securities.iter().try_for_each(|security| {
2520                    if security_definitions.contains_key(security) {
2521                        Ok(())
2522                    } else {
2523                        Err(Error::UndefinedSecurity(security.clone()))
2524                    }
2525                })
2526            })
2527            .transpose()?;
2528    }
2529
2530    Ok(())
2531}
2532
2533pub(crate) struct UncheckedInteractionAffordance<Other: ExtendableThing> {
2534    attype: Option<Vec<String>>,
2535    title: Option<String>,
2536    titles: Option<MultiLanguageBuilder<String>>,
2537    description: Option<String>,
2538    descriptions: Option<MultiLanguageBuilder<String>>,
2539    forms: Vec<Form<Other>>,
2540    uri_variables: Option<UncheckedDataSchemaMap<Other>>,
2541    other: Other::InteractionAffordance,
2542}
2543
2544impl<Other: ExtendableThing, OtherInteractionAffordance>
2545    TryFrom<InteractionAffordanceBuilder<Other, OtherInteractionAffordance>>
2546    for InteractionAffordance<Other>
2547where
2548    Other: ExtendableThing,
2549    OtherInteractionAffordance: Into<Other::InteractionAffordance>,
2550{
2551    type Error = Error;
2552
2553    fn try_from(
2554        builder: InteractionAffordanceBuilder<Other, OtherInteractionAffordance>,
2555    ) -> Result<Self, Self::Error> {
2556        let interaction: UncheckedInteractionAffordance<_> = builder.into();
2557        interaction.try_into()
2558    }
2559}
2560
2561impl<Other: ExtendableThing> TryFrom<UncheckedInteractionAffordance<Other>>
2562    for InteractionAffordance<Other>
2563{
2564    type Error = Error;
2565
2566    fn try_from(affordance: UncheckedInteractionAffordance<Other>) -> Result<Self, Self::Error> {
2567        let UncheckedInteractionAffordance {
2568            attype,
2569            title,
2570            titles,
2571            description,
2572            descriptions,
2573            forms,
2574            uri_variables,
2575            other,
2576        } = affordance;
2577
2578        let titles = titles.map(|titles| titles.build()).transpose()?;
2579        let descriptions = descriptions
2580            .map(|descriptions| descriptions.build())
2581            .transpose()?;
2582        let uri_variables = uri_variables
2583            .map(|uri_variables| {
2584                uri_variables
2585                    .into_iter()
2586                    .map(|(key, value)| value.try_into().map(|value| (key, value)))
2587                    .collect()
2588            })
2589            .transpose()?;
2590
2591        Ok(Self {
2592            attype,
2593            title,
2594            titles,
2595            description,
2596            descriptions,
2597            forms,
2598            uri_variables,
2599            other,
2600        })
2601    }
2602}
2603
2604pub(crate) trait BuildableAffordance {
2605    type Target;
2606
2607    fn build(self) -> Result<Self::Target, Error>;
2608}
2609
2610impl<Other, DS, AS, OS> BuildableAffordance for UsablePropertyAffordanceBuilder<Other>
2611where
2612    Other: ExtendableThing<DataSchema = DS, ArraySchema = AS, ObjectSchema = OS>,
2613{
2614    type Target = PropertyAffordance<Other>;
2615
2616    fn build(self) -> Result<Self::Target, Error> {
2617        let Self {
2618            interaction,
2619            info,
2620            data_schema,
2621            observable,
2622            other,
2623        } = self;
2624
2625        let PartialInteractionAffordanceBuilder {
2626            forms,
2627            uri_variables,
2628            other: other_interaction,
2629        } = interaction;
2630
2631        let PartialDataSchema {
2632            constant,
2633            default,
2634            unit,
2635            one_of,
2636            enumeration,
2637            read_only,
2638            write_only,
2639            format,
2640            subtype,
2641            other: data_schema_other,
2642        } = data_schema;
2643
2644        let HumanReadableInfo {
2645            attype,
2646            title,
2647            titles,
2648            description,
2649            descriptions,
2650        } = info;
2651
2652        let titles = titles.map(|titles| titles.build()).transpose()?;
2653        let descriptions = descriptions
2654            .map(|descriptions| descriptions.build())
2655            .transpose()?;
2656        let forms = forms.into_iter().map(Into::into).collect();
2657        let uri_variables = uri_variables
2658            .is_empty()
2659            .not()
2660            .then(|| {
2661                uri_variables
2662                    .into_iter()
2663                    .map(|(key, value)| value.try_into().map(|value| (key, value)))
2664                    .collect()
2665            })
2666            .transpose()?;
2667        let one_of = one_of
2668            .map(|one_of| one_of.into_iter().map(TryInto::try_into).collect())
2669            .transpose()?;
2670        let subtype = subtype.map(TryInto::try_into).transpose()?;
2671
2672        let interaction = InteractionAffordance {
2673            attype: attype.clone(),
2674            title: title.clone(),
2675            titles: titles.clone(),
2676            description: description.clone(),
2677            descriptions: descriptions.clone(),
2678            forms,
2679            uri_variables,
2680            other: other_interaction,
2681        };
2682
2683        let data_schema = DataSchema {
2684            attype,
2685            title,
2686            titles,
2687            description,
2688            descriptions,
2689            constant,
2690            default,
2691            unit,
2692            one_of,
2693            enumeration,
2694            read_only,
2695            write_only,
2696            format,
2697            subtype,
2698            other: data_schema_other,
2699        };
2700
2701        Ok(PropertyAffordance {
2702            interaction,
2703            data_schema,
2704            observable,
2705            other,
2706        })
2707    }
2708}
2709
2710impl<Other, OtherInteractionAffordance, OtherEventAffordance> BuildableAffordance
2711    for EventAffordanceBuilder<Other, OtherInteractionAffordance, OtherEventAffordance>
2712where
2713    Other: ExtendableThing,
2714    OtherInteractionAffordance: Into<Other::InteractionAffordance>,
2715    OtherEventAffordance: Into<Other::EventAffordance>,
2716{
2717    type Target = EventAffordance<Other>;
2718
2719    fn build(self) -> Result<Self::Target, Error> {
2720        let Self {
2721            interaction,
2722            subscription,
2723            data,
2724            cancellation,
2725            data_response,
2726            other,
2727        } = self;
2728
2729        let interaction = interaction.try_into()?;
2730        let subscription = subscription
2731            .map(|subscription| subscription.try_into())
2732            .transpose()?;
2733        let data = data.map(|data| data.try_into()).transpose()?;
2734        let cancellation = cancellation
2735            .map(|cancellation| cancellation.try_into())
2736            .transpose()?;
2737        let data_response = data_response
2738            .map(|data_response| data_response.try_into())
2739            .transpose()?;
2740        let other = other.into();
2741
2742        Ok(Self::Target {
2743            interaction,
2744            subscription,
2745            data,
2746            cancellation,
2747            data_response,
2748            other,
2749        })
2750    }
2751}
2752
2753impl<Other, OtherInteractionAffordance, OtherActionAffordance> BuildableAffordance
2754    for ActionAffordanceBuilder<Other, OtherInteractionAffordance, OtherActionAffordance>
2755where
2756    Other: ExtendableThing,
2757    OtherInteractionAffordance: Into<Other::InteractionAffordance>,
2758    OtherActionAffordance: Into<Other::ActionAffordance>,
2759{
2760    type Target = ActionAffordance<Other>;
2761
2762    fn build(self) -> Result<Self::Target, Error> {
2763        let Self {
2764            interaction,
2765            input,
2766            output,
2767            safe,
2768            idempotent,
2769            synchronous,
2770            other,
2771        } = self;
2772
2773        let interaction = interaction.try_into()?;
2774        let input = input.map(|input| input.try_into()).transpose()?;
2775        let output = output.map(|output| output.try_into()).transpose()?;
2776        let other = other.into();
2777
2778        Ok(Self::Target {
2779            interaction,
2780            input,
2781            output,
2782            safe,
2783            idempotent,
2784            synchronous,
2785            other,
2786        })
2787    }
2788}
2789
2790#[cfg(test)]
2791mod test {
2792    use alloc::{borrow::ToOwned, boxed::Box, string::*, vec};
2793
2794    use serde::{Deserialize, Serialize};
2795    use serde_json::json;
2796
2797    use crate::{
2798        builder::data_schema::{
2799            BuildableDataSchema, NumberDataSchemaBuilderLike, PartialDataSchemaBuilder,
2800        },
2801        hlist::{Cons, Nil},
2802        thing::{
2803            ArraySchema, BoxedElemOrVec, DataSchemaFromOther, DataSchemaSubtype,
2804            DefaultedFormOperations, FormOperation, Minimum, NumberSchema,
2805        },
2806    };
2807
2808    use super::*;
2809
2810    #[test]
2811    fn empty_iteraction() {
2812        let affordance: UncheckedInteractionAffordance<Nil> =
2813            InteractionAffordanceBuilder::<Nil, ()>::default().into();
2814        let affordance: InteractionAffordance<_> = affordance.try_into().unwrap();
2815        assert_eq!(affordance, InteractionAffordance::default());
2816    }
2817
2818    #[test]
2819    fn full_interaction() {
2820        let affordance: UncheckedInteractionAffordance<Nil> =
2821            InteractionAffordanceBuilder::<Nil, ()>::default()
2822                .attype("attype1")
2823                .attype("attype2")
2824                .title("title")
2825                .titles(|b| b.add("it", "title_it").add("en", "title_en"))
2826                .description("description")
2827                .descriptions(|b| b.add("it", "description_it").add("en", "description_en"))
2828                .form(|b| b.href("form1_href").content_type("content_type"))
2829                .form(|b| {
2830                    b.op(FormOperation::WriteProperty)
2831                        .op(FormOperation::ReadProperty)
2832                        .href("form2_href")
2833                })
2834                .uri_variable("uri1", |b| b.finish_extend().number())
2835                .uri_variable("uri2", |b| b.finish_extend().integer())
2836                .into();
2837        let affordance: InteractionAffordance<Nil> = affordance.try_into().unwrap();
2838
2839        assert_eq!(
2840            affordance,
2841            InteractionAffordance {
2842                attype: Some(vec!["attype1".to_string(), "attype2".to_string()]),
2843                title: Some("title".to_string()),
2844                titles: Some(
2845                    [("it", "title_it"), ("en", "title_en"),]
2846                        .into_iter()
2847                        .map(|(k, v)| (k.parse().unwrap(), v.to_string()))
2848                        .collect()
2849                ),
2850                description: Some("description".to_string()),
2851                descriptions: Some(
2852                    [("it", "description_it"), ("en", "description_en"),]
2853                        .into_iter()
2854                        .map(|(k, v)| (k.parse().unwrap(), v.to_string()))
2855                        .collect()
2856                ),
2857                forms: vec![
2858                    Form {
2859                        op: DefaultedFormOperations::Default,
2860                        href: "form1_href".to_string(),
2861                        content_type: Some("content_type".to_string()),
2862                        ..Default::default()
2863                    },
2864                    Form {
2865                        op: DefaultedFormOperations::Custom(vec![
2866                            FormOperation::WriteProperty,
2867                            FormOperation::ReadProperty,
2868                        ]),
2869                        href: "form2_href".to_string(),
2870                        ..Default::default()
2871                    },
2872                ],
2873                uri_variables: Some(
2874                    [
2875                        (
2876                            "uri1".to_string(),
2877                            DataSchema {
2878                                subtype: Some(DataSchemaSubtype::Number(Default::default())),
2879                                ..Default::default()
2880                            },
2881                        ),
2882                        (
2883                            "uri2".to_string(),
2884                            DataSchema {
2885                                subtype: Some(DataSchemaSubtype::Integer(Default::default())),
2886                                ..Default::default()
2887                            },
2888                        ),
2889                    ]
2890                    .into_iter()
2891                    .collect()
2892                ),
2893                other: Default::default(),
2894            },
2895        );
2896    }
2897
2898    #[test]
2899    fn property_basic() {
2900        let affordance_builder: UsablePropertyAffordanceBuilder<Nil> = PropertyAffordanceBuilder::<
2901            Nil,
2902            PartialDataSchemaBuilder<_, _, _, _>,
2903            (),
2904            (),
2905        >::default()
2906        .title("property")
2907        .default_value(["hello", "world"].as_slice())
2908        .number()
2909        .observable(true)
2910        .form(|b| b.href("href"))
2911        .unit("cm")
2912        .read_only()
2913        .minimum(0.)
2914        .uri_variable("test", |b| b.finish_extend().bool())
2915        .into_usable();
2916
2917        let affordance: PropertyAffordance<Nil> = affordance_builder.build().unwrap();
2918
2919        assert_eq!(
2920            affordance,
2921            PropertyAffordance {
2922                interaction: InteractionAffordance {
2923                    title: Some("property".to_owned()),
2924                    forms: vec![Form {
2925                        href: "href".to_owned(),
2926                        ..Default::default()
2927                    }],
2928                    uri_variables: Some(
2929                        [(
2930                            "test".to_owned(),
2931                            DataSchemaFromOther::<Nil> {
2932                                subtype: Some(DataSchemaSubtype::Boolean),
2933                                ..Default::default()
2934                            }
2935                        )]
2936                        .into_iter()
2937                        .collect()
2938                    ),
2939                    ..Default::default()
2940                },
2941                data_schema: DataSchemaFromOther::<Nil> {
2942                    title: Some("property".to_owned()),
2943                    unit: Some("cm".to_owned()),
2944                    default: Some(json! { ["hello", "world"] }),
2945                    read_only: true,
2946                    subtype: Some(DataSchemaSubtype::Number(NumberSchema {
2947                        minimum: Some(Minimum::Inclusive(0.)),
2948                        ..Default::default()
2949                    })),
2950                    ..Default::default()
2951                },
2952                observable: Some(true),
2953                other: Nil,
2954            },
2955        );
2956    }
2957
2958    #[test]
2959    fn property_enum() {
2960        let affordance_builder: UsablePropertyAffordanceBuilder<Nil> = PropertyAffordanceBuilder::<
2961            Nil,
2962            PartialDataSchemaBuilder<_, _, _, _>,
2963            (),
2964            (),
2965        >::default()
2966        .title("property")
2967        .enumeration("enum1")
2968        .write_only()
2969        .enumeration("enum2")
2970        .observable(true)
2971        .form(|b| b.href("href"))
2972        .unit("cm")
2973        .into_usable();
2974
2975        let affordance: PropertyAffordance<Nil> = affordance_builder.build().unwrap();
2976
2977        assert_eq!(
2978            affordance,
2979            PropertyAffordance {
2980                interaction: InteractionAffordance {
2981                    title: Some("property".to_owned()),
2982                    forms: vec![Form {
2983                        href: "href".to_owned(),
2984                        ..Default::default()
2985                    }],
2986                    ..Default::default()
2987                },
2988                data_schema: DataSchemaFromOther::<Nil> {
2989                    title: Some("property".to_owned()),
2990                    unit: Some("cm".to_owned()),
2991                    enumeration: Some(vec!["enum1".into(), "enum2".into()]),
2992                    write_only: true,
2993                    ..Default::default()
2994                },
2995                observable: Some(true),
2996                other: Nil,
2997            },
2998        );
2999    }
3000
3001    #[test]
3002    fn property_one_of() {
3003        let affordance_builder: UsablePropertyAffordanceBuilder<Nil> = PropertyAffordanceBuilder::<
3004            Nil,
3005            PartialDataSchemaBuilder<_, _, _, _>,
3006            (),
3007            (),
3008        >::default()
3009        .title("property")
3010        .one_of(|b| b.finish_extend().number())
3011        .one_of(|b| b.finish_extend().integer())
3012        .observable(true)
3013        .form(|b| b.href("href"))
3014        .unit("cm")
3015        .into_usable();
3016
3017        let affordance: PropertyAffordance<Nil> = affordance_builder.build().unwrap();
3018
3019        assert_eq!(
3020            affordance,
3021            PropertyAffordance {
3022                interaction: InteractionAffordance {
3023                    title: Some("property".to_owned()),
3024                    forms: vec![Form {
3025                        href: "href".to_owned(),
3026                        ..Default::default()
3027                    }],
3028                    ..Default::default()
3029                },
3030                data_schema: DataSchemaFromOther::<Nil> {
3031                    title: Some("property".to_owned()),
3032                    unit: Some("cm".to_owned()),
3033                    one_of: Some(vec![
3034                        DataSchemaFromOther::<Nil> {
3035                            subtype: Some(DataSchemaSubtype::Number(Default::default())),
3036                            ..Default::default()
3037                        },
3038                        DataSchemaFromOther::<Nil> {
3039                            subtype: Some(DataSchemaSubtype::Integer(Default::default())),
3040                            ..Default::default()
3041                        },
3042                    ]),
3043                    ..Default::default()
3044                },
3045                observable: Some(true),
3046                other: Nil,
3047            },
3048        );
3049    }
3050
3051    #[test]
3052    fn action_partial() {
3053        let affordance_builder: UsableActionAffordanceBuilder<Nil> =
3054            ActionAffordanceBuilder::<Nil, (), ()>::default()
3055                .title("action")
3056                .safe()
3057                .input(|b| {
3058                    b.finish_extend()
3059                        .number()
3060                        .unit("cm")
3061                        .read_only()
3062                        .minimum(0.)
3063                })
3064                .form(|b| b.href("href"))
3065                .into_usable();
3066
3067        let affordance: ActionAffordance<Nil> = affordance_builder.build().unwrap();
3068
3069        assert_eq!(
3070            affordance,
3071            ActionAffordance {
3072                interaction: InteractionAffordance {
3073                    title: Some("action".to_owned()),
3074                    forms: vec![Form {
3075                        href: "href".to_owned(),
3076                        ..Default::default()
3077                    }],
3078                    ..Default::default()
3079                },
3080                input: Some(DataSchemaFromOther::<Nil> {
3081                    unit: Some("cm".to_owned()),
3082                    read_only: true,
3083                    subtype: Some(DataSchemaSubtype::Number(NumberSchema {
3084                        minimum: Some(Minimum::Inclusive(0.)),
3085                        ..Default::default()
3086                    })),
3087                    ..Default::default()
3088                }),
3089                safe: true,
3090                ..Default::default()
3091            },
3092        );
3093    }
3094
3095    #[test]
3096    fn action_full() {
3097        let affordance_builder: UsableActionAffordanceBuilder<Nil> =
3098            ActionAffordanceBuilder::<Nil, (), ()>::default()
3099                .title("action")
3100                .safe()
3101                .input(|b| {
3102                    b.finish_extend()
3103                        .number()
3104                        .unit("cm")
3105                        .read_only()
3106                        .minimum(0.)
3107                })
3108                .idempotent()
3109                .output(|b| {
3110                    b.finish_extend()
3111                        .number()
3112                        .unit("cm")
3113                        .read_only()
3114                        .minimum(0.)
3115                })
3116                .form(|b| b.href("href"))
3117                .synchronous(true)
3118                .into_usable();
3119
3120        let affordance: ActionAffordance<Nil> = affordance_builder.build().unwrap();
3121
3122        assert_eq!(
3123            affordance,
3124            ActionAffordance {
3125                interaction: InteractionAffordance {
3126                    title: Some("action".to_owned()),
3127                    forms: vec![Form {
3128                        href: "href".to_owned(),
3129                        ..Default::default()
3130                    }],
3131                    ..Default::default()
3132                },
3133                input: Some(DataSchemaFromOther::<Nil> {
3134                    unit: Some("cm".to_owned()),
3135                    read_only: true,
3136                    subtype: Some(DataSchemaSubtype::Number(NumberSchema {
3137                        minimum: Some(Minimum::Inclusive(0.)),
3138                        ..Default::default()
3139                    })),
3140                    ..Default::default()
3141                }),
3142                output: Some(DataSchemaFromOther::<Nil> {
3143                    unit: Some("cm".to_owned()),
3144                    read_only: true,
3145                    subtype: Some(DataSchemaSubtype::Number(NumberSchema {
3146                        minimum: Some(Minimum::Inclusive(0.)),
3147                        ..Default::default()
3148                    })),
3149                    ..Default::default()
3150                }),
3151                safe: true,
3152                idempotent: true,
3153                synchronous: Some(true),
3154                other: Nil,
3155            },
3156        );
3157    }
3158
3159    #[test]
3160    fn event_partial() {
3161        let affordance_builder: UsableEventAffordanceBuilder<Nil> =
3162            EventAffordanceBuilder::<Nil, (), ()>::default()
3163                .title("event")
3164                .data(|b| {
3165                    b.finish_extend()
3166                        .number()
3167                        .unit("cm")
3168                        .read_only()
3169                        .minimum(0.)
3170                })
3171                .form(|b| b.href("href"))
3172                .into_usable();
3173
3174        let affordance: EventAffordance<Nil> = affordance_builder.build().unwrap();
3175
3176        assert_eq!(
3177            affordance,
3178            EventAffordance {
3179                interaction: InteractionAffordance {
3180                    title: Some("event".to_owned()),
3181                    forms: vec![Form {
3182                        href: "href".to_owned(),
3183                        ..Default::default()
3184                    }],
3185                    ..Default::default()
3186                },
3187                data: Some(DataSchemaFromOther::<Nil> {
3188                    unit: Some("cm".to_owned()),
3189                    read_only: true,
3190                    subtype: Some(DataSchemaSubtype::Number(NumberSchema {
3191                        minimum: Some(Minimum::Inclusive(0.)),
3192                        ..Default::default()
3193                    })),
3194                    ..Default::default()
3195                }),
3196                ..Default::default()
3197            },
3198        );
3199    }
3200
3201    #[test]
3202    fn event_full() {
3203        let affordance_builder: UsableEventAffordanceBuilder<Nil> =
3204            EventAffordanceBuilder::<Nil, (), ()>::default()
3205                .title("event")
3206                .cancellation(|b| b.finish_extend().integer())
3207                .data(|b| {
3208                    b.finish_extend()
3209                        .number()
3210                        .unit("cm")
3211                        .read_only()
3212                        .minimum(0.)
3213                })
3214                .subscription(|b| b.finish_extend().bool())
3215                .data_response(|b| b.finish_extend().string())
3216                .form(|b| b.href("href"))
3217                .into_usable();
3218
3219        let affordance: EventAffordance<Nil> = affordance_builder.build().unwrap();
3220
3221        assert_eq!(
3222            affordance,
3223            EventAffordance {
3224                interaction: InteractionAffordance {
3225                    title: Some("event".to_owned()),
3226                    forms: vec![Form {
3227                        href: "href".to_owned(),
3228                        ..Default::default()
3229                    }],
3230                    ..Default::default()
3231                },
3232                subscription: Some(DataSchemaFromOther::<Nil> {
3233                    subtype: Some(DataSchemaSubtype::Boolean),
3234                    ..Default::default()
3235                }),
3236                data: Some(DataSchemaFromOther::<Nil> {
3237                    unit: Some("cm".to_owned()),
3238                    read_only: true,
3239                    subtype: Some(DataSchemaSubtype::Number(NumberSchema {
3240                        minimum: Some(Minimum::Inclusive(0.)),
3241                        ..Default::default()
3242                    })),
3243                    ..Default::default()
3244                }),
3245                cancellation: Some(DataSchemaFromOther::<Nil> {
3246                    subtype: Some(DataSchemaSubtype::Integer(Default::default())),
3247                    ..Default::default()
3248                }),
3249                data_response: Some(DataSchemaFromOther::<Nil> {
3250                    subtype: Some(DataSchemaSubtype::String(Default::default())),
3251                    ..Default::default()
3252                }),
3253                other: Nil,
3254            },
3255        );
3256    }
3257
3258    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3259    struct A(i32);
3260    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3261    struct B(String);
3262
3263    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3264    struct ThingExtA {}
3265
3266    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3267    struct InteractionAffordanceExtA {
3268        a: A,
3269    }
3270
3271    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3272    struct PropertyAffordanceExtA {
3273        b: A,
3274    }
3275
3276    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3277    struct ActionAffordanceExtA {
3278        b: A,
3279    }
3280
3281    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3282    struct EventAffordanceExtA {
3283        c: A,
3284    }
3285
3286    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3287    struct FormExtA {
3288        d: A,
3289    }
3290
3291    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3292    struct ExpectedResponseExtA {
3293        e: A,
3294    }
3295
3296    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3297    struct DataSchemaExtA {
3298        f: A,
3299    }
3300
3301    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3302    struct ThingExtB {}
3303
3304    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3305    struct InteractionAffordanceExtB {
3306        g: B,
3307    }
3308
3309    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3310    struct PropertyAffordanceExtB {
3311        h: B,
3312    }
3313
3314    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3315    struct ActionAffordanceExtB {
3316        i: B,
3317    }
3318
3319    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3320    struct EventAffordanceExtB {
3321        j: B,
3322    }
3323
3324    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3325    struct FormExtB {
3326        k: B,
3327    }
3328
3329    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3330    struct ExpectedResponseExtB {
3331        l: B,
3332    }
3333
3334    #[derive(Debug, PartialEq, Serialize, Deserialize)]
3335    struct DataSchemaExtB {
3336        m: B,
3337    }
3338
3339    impl ExtendableThing for ThingExtA {
3340        type InteractionAffordance = InteractionAffordanceExtA;
3341        type PropertyAffordance = PropertyAffordanceExtA;
3342        type ActionAffordance = ActionAffordanceExtA;
3343        type EventAffordance = EventAffordanceExtA;
3344        type Form = FormExtA;
3345        type ExpectedResponse = ExpectedResponseExtA;
3346        type DataSchema = DataSchemaExtA;
3347        type ObjectSchema = ();
3348        type ArraySchema = ();
3349    }
3350
3351    impl ExtendableThing for ThingExtB {
3352        type InteractionAffordance = InteractionAffordanceExtB;
3353        type PropertyAffordance = PropertyAffordanceExtB;
3354        type ActionAffordance = ActionAffordanceExtB;
3355        type EventAffordance = EventAffordanceExtB;
3356        type Form = FormExtB;
3357        type ExpectedResponse = ExpectedResponseExtB;
3358        type DataSchema = DataSchemaExtB;
3359        type ObjectSchema = ();
3360        type ArraySchema = ();
3361    }
3362
3363    #[test]
3364    fn extend_interaction() {
3365        let affordance: UncheckedInteractionAffordance<Cons<ThingExtB, Cons<ThingExtA, Nil>>> =
3366            InteractionAffordanceBuilder::<Cons<ThingExtB, Cons<ThingExtA, Nil>>, _>::empty()
3367                .title("title")
3368                .ext(InteractionAffordanceExtA { a: A(1) })
3369                .uri_variable("x", |b| {
3370                    b.ext(DataSchemaExtA { f: A(2) })
3371                        .ext_with(|| DataSchemaExtB {
3372                            m: B("a".to_string()),
3373                        })
3374                        .finish_extend()
3375                        .null()
3376                })
3377                .ext_with(|| InteractionAffordanceExtB {
3378                    g: B("b".to_string()),
3379                })
3380                .form(|b| {
3381                    b.ext(FormExtA { d: A(3) })
3382                        .ext(FormExtB {
3383                            k: B("c".to_string()),
3384                        })
3385                        .href("href")
3386                })
3387                .into();
3388        let affordance: InteractionAffordance<_> = affordance.try_into().unwrap();
3389
3390        assert_eq!(
3391            affordance,
3392            InteractionAffordance {
3393                title: Some("title".to_string()),
3394                uri_variables: Some(
3395                    [(
3396                        "x".to_string(),
3397                        DataSchema {
3398                            subtype: Some(DataSchemaSubtype::Null),
3399                            other: Nil::cons(DataSchemaExtA { f: A(2) }).cons(DataSchemaExtB {
3400                                m: B("a".to_string())
3401                            }),
3402                            attype: Default::default(),
3403                            title: Default::default(),
3404                            titles: Default::default(),
3405                            description: Default::default(),
3406                            descriptions: Default::default(),
3407                            constant: Default::default(),
3408                            default: Default::default(),
3409                            unit: Default::default(),
3410                            one_of: Default::default(),
3411                            enumeration: Default::default(),
3412                            read_only: Default::default(),
3413                            write_only: Default::default(),
3414                            format: Default::default(),
3415                        }
3416                    )]
3417                    .into_iter()
3418                    .collect()
3419                ),
3420                forms: vec![Form {
3421                    href: "href".to_string(),
3422                    other: Nil::cons(FormExtA { d: A(3) }).cons(FormExtB {
3423                        k: B("c".to_string())
3424                    }),
3425                    op: Default::default(),
3426                    content_type: Default::default(),
3427                    content_coding: Default::default(),
3428                    subprotocol: Default::default(),
3429                    security: Default::default(),
3430                    scopes: Default::default(),
3431                    response: Default::default(),
3432                    additional_responses: Default::default(),
3433                }],
3434                other: Nil::cons(InteractionAffordanceExtA { a: A(1) }).cons(
3435                    InteractionAffordanceExtB {
3436                        g: B("b".to_string())
3437                    }
3438                ),
3439                attype: Default::default(),
3440                titles: Default::default(),
3441                description: Default::default(),
3442                descriptions: Default::default(),
3443            },
3444        );
3445    }
3446
3447    #[test]
3448    fn extend_property() {
3449        let builder: UsablePropertyAffordanceBuilder<Cons<ThingExtB, Cons<ThingExtA, Nil>>> =
3450            PropertyAffordanceBuilder::<Cons<ThingExtB, Cons<ThingExtA, Nil>>, _, _, _>::empty()
3451                .ext(PropertyAffordanceExtA { b: A(1) })
3452                .title("title")
3453                .ext_interaction(InteractionAffordanceExtA { a: A(2) })
3454                .ext_data_schema(DataSchemaExtA { f: A(4) })
3455                .uri_variable("x", |b| {
3456                    b.ext(DataSchemaExtA { f: A(3) })
3457                        .ext_with(|| DataSchemaExtB {
3458                            m: B("a".to_string()),
3459                        })
3460                        .finish_extend()
3461                        .null()
3462                })
3463                .ext_data_schema_with(|| DataSchemaExtB {
3464                    m: B("d".to_string()),
3465                })
3466                .ext_interaction_with(|| InteractionAffordanceExtB {
3467                    g: B("b".to_string()),
3468                })
3469                .finish_extend_data_schema()
3470                .ext_with(|| PropertyAffordanceExtB {
3471                    h: B("c".to_string()),
3472                })
3473                .null()
3474                .into_usable();
3475        let affordance: PropertyAffordance<Cons<ThingExtB, Cons<ThingExtA, Nil>>> =
3476            builder.build().unwrap();
3477
3478        assert_eq!(
3479            affordance,
3480            PropertyAffordance {
3481                interaction: InteractionAffordance {
3482                    other: Nil::cons(InteractionAffordanceExtA { a: A(2) }).cons(
3483                        InteractionAffordanceExtB {
3484                            g: B("b".to_string())
3485                        }
3486                    ),
3487                    uri_variables: Some(
3488                        [(
3489                            "x".to_string(),
3490                            DataSchema {
3491                                subtype: Some(DataSchemaSubtype::Null),
3492                                other: Nil::cons(DataSchemaExtA { f: A(3) }).cons(DataSchemaExtB {
3493                                    m: B("a".to_string())
3494                                }),
3495                                attype: Default::default(),
3496                                title: Default::default(),
3497                                titles: Default::default(),
3498                                description: Default::default(),
3499                                descriptions: Default::default(),
3500                                constant: Default::default(),
3501                                default: Default::default(),
3502                                unit: Default::default(),
3503                                one_of: Default::default(),
3504                                enumeration: Default::default(),
3505                                read_only: Default::default(),
3506                                write_only: Default::default(),
3507                                format: Default::default(),
3508                            }
3509                        )]
3510                        .into_iter()
3511                        .collect()
3512                    ),
3513                    attype: Default::default(),
3514                    title: Some("title".to_string()),
3515                    titles: Default::default(),
3516                    description: Default::default(),
3517                    descriptions: Default::default(),
3518                    forms: Default::default(),
3519                },
3520                data_schema: DataSchema {
3521                    title: Some("title".to_string()),
3522                    subtype: Some(DataSchemaSubtype::Null),
3523                    other: Nil::cons(DataSchemaExtA { f: A(4) }).cons(DataSchemaExtB {
3524                        m: B("d".to_string())
3525                    }),
3526                    attype: Default::default(),
3527                    titles: Default::default(),
3528                    description: Default::default(),
3529                    descriptions: Default::default(),
3530                    constant: Default::default(),
3531                    default: Default::default(),
3532                    unit: Default::default(),
3533                    one_of: Default::default(),
3534                    enumeration: Default::default(),
3535                    read_only: Default::default(),
3536                    write_only: Default::default(),
3537                    format: Default::default(),
3538                },
3539                other: Nil::cons(PropertyAffordanceExtA { b: A(1) }).cons(PropertyAffordanceExtB {
3540                    h: B("c".to_string())
3541                }),
3542                observable: Default::default(),
3543            }
3544        );
3545    }
3546
3547    #[test]
3548    fn extend_event() {
3549        let builder: UsableEventAffordanceBuilder<Cons<ThingExtB, Cons<ThingExtA, Nil>>> =
3550            EventAffordanceBuilder::<Cons<ThingExtB, Cons<ThingExtA, Nil>>, _, _>::empty()
3551                .title("title")
3552                .ext_interaction(InteractionAffordanceExtA { a: A(1) })
3553                .uri_variable("x", |b| {
3554                    b.ext(DataSchemaExtA { f: A(2) })
3555                        .ext_with(|| DataSchemaExtB {
3556                            m: B("a".to_string()),
3557                        })
3558                        .finish_extend()
3559                        .null()
3560                })
3561                .ext_interaction_with(|| InteractionAffordanceExtB {
3562                    g: B("b".to_string()),
3563                })
3564                .ext(EventAffordanceExtA { c: A(3) })
3565                .ext_with(|| EventAffordanceExtB {
3566                    j: B("c".to_string()),
3567                })
3568                .subscription(|b| {
3569                    b.ext(DataSchemaExtA { f: A(4) })
3570                        .ext_with(|| DataSchemaExtB {
3571                            m: B("d".to_string()),
3572                        })
3573                        .finish_extend()
3574                        .null()
3575                })
3576                .into_usable();
3577        let affordance: EventAffordance<Cons<ThingExtB, Cons<ThingExtA, Nil>>> =
3578            builder.build().unwrap();
3579
3580        assert_eq!(
3581            affordance,
3582            EventAffordance {
3583                interaction: InteractionAffordance {
3584                    title: Some("title".to_string()),
3585                    uri_variables: Some(
3586                        [(
3587                            "x".to_string(),
3588                            DataSchema {
3589                                subtype: Some(DataSchemaSubtype::Null),
3590                                other: Nil::cons(DataSchemaExtA { f: A(2) }).cons(DataSchemaExtB {
3591                                    m: B("a".to_string())
3592                                }),
3593                                attype: Default::default(),
3594                                title: Default::default(),
3595                                titles: Default::default(),
3596                                description: Default::default(),
3597                                descriptions: Default::default(),
3598                                constant: Default::default(),
3599                                default: Default::default(),
3600                                unit: Default::default(),
3601                                one_of: Default::default(),
3602                                enumeration: Default::default(),
3603                                read_only: Default::default(),
3604                                write_only: Default::default(),
3605                                format: Default::default(),
3606                            }
3607                        )]
3608                        .into_iter()
3609                        .collect()
3610                    ),
3611                    other: Nil::cons(InteractionAffordanceExtA { a: A(1) }).cons(
3612                        InteractionAffordanceExtB {
3613                            g: B("b".to_string())
3614                        }
3615                    ),
3616                    attype: Default::default(),
3617                    titles: Default::default(),
3618                    description: Default::default(),
3619                    descriptions: Default::default(),
3620                    forms: Default::default(),
3621                },
3622                subscription: Some(DataSchema {
3623                    subtype: Some(DataSchemaSubtype::Null),
3624                    other: Nil::cons(DataSchemaExtA { f: A(4) }).cons(DataSchemaExtB {
3625                        m: B("d".to_string())
3626                    }),
3627                    attype: Default::default(),
3628                    title: Default::default(),
3629                    titles: Default::default(),
3630                    description: Default::default(),
3631                    descriptions: Default::default(),
3632                    constant: Default::default(),
3633                    default: Default::default(),
3634                    unit: Default::default(),
3635                    one_of: Default::default(),
3636                    enumeration: Default::default(),
3637                    read_only: Default::default(),
3638                    write_only: Default::default(),
3639                    format: Default::default(),
3640                }),
3641                other: Nil::cons(EventAffordanceExtA { c: A(3) }).cons(EventAffordanceExtB {
3642                    j: B("c".to_string())
3643                }),
3644                data: Default::default(),
3645                data_response: Default::default(),
3646                cancellation: Default::default(),
3647            },
3648        );
3649    }
3650
3651    #[test]
3652    fn extend_action() {
3653        let builder: UsableActionAffordanceBuilder<Cons<ThingExtB, Cons<ThingExtA, Nil>>> =
3654            ActionAffordanceBuilder::<Cons<ThingExtB, Cons<ThingExtA, Nil>>, _, _>::empty()
3655                .title("title")
3656                .ext_interaction(InteractionAffordanceExtA { a: A(1) })
3657                .uri_variable("x", |b| {
3658                    b.ext(DataSchemaExtA { f: A(2) })
3659                        .ext_with(|| DataSchemaExtB {
3660                            m: B("a".to_string()),
3661                        })
3662                        .finish_extend()
3663                        .null()
3664                })
3665                .ext_interaction_with(|| InteractionAffordanceExtB {
3666                    g: B("b".to_string()),
3667                })
3668                .ext(ActionAffordanceExtA { b: A(3) })
3669                .ext_with(|| ActionAffordanceExtB {
3670                    i: B("c".to_string()),
3671                })
3672                .input(|b| {
3673                    b.ext(DataSchemaExtA { f: A(4) })
3674                        .ext_with(|| DataSchemaExtB {
3675                            m: B("d".to_string()),
3676                        })
3677                        .finish_extend()
3678                        .null()
3679                })
3680                .into_usable();
3681        let affordance: ActionAffordance<Cons<ThingExtB, Cons<ThingExtA, Nil>>> =
3682            builder.build().unwrap();
3683
3684        assert_eq!(
3685            affordance,
3686            ActionAffordance {
3687                interaction: InteractionAffordance {
3688                    title: Some("title".to_string()),
3689                    uri_variables: Some(
3690                        [(
3691                            "x".to_string(),
3692                            DataSchema {
3693                                subtype: Some(DataSchemaSubtype::Null),
3694                                other: Nil::cons(DataSchemaExtA { f: A(2) }).cons(DataSchemaExtB {
3695                                    m: B("a".to_string())
3696                                }),
3697                                attype: Default::default(),
3698                                title: Default::default(),
3699                                titles: Default::default(),
3700                                description: Default::default(),
3701                                descriptions: Default::default(),
3702                                constant: Default::default(),
3703                                default: Default::default(),
3704                                unit: Default::default(),
3705                                one_of: Default::default(),
3706                                enumeration: Default::default(),
3707                                read_only: Default::default(),
3708                                write_only: Default::default(),
3709                                format: Default::default(),
3710                            }
3711                        )]
3712                        .into_iter()
3713                        .collect()
3714                    ),
3715                    other: Nil::cons(InteractionAffordanceExtA { a: A(1) }).cons(
3716                        InteractionAffordanceExtB {
3717                            g: B("b".to_string())
3718                        }
3719                    ),
3720                    attype: Default::default(),
3721                    titles: Default::default(),
3722                    description: Default::default(),
3723                    descriptions: Default::default(),
3724                    forms: Default::default(),
3725                },
3726                input: Some(DataSchema {
3727                    subtype: Some(DataSchemaSubtype::Null),
3728                    other: Nil::cons(DataSchemaExtA { f: A(4) }).cons(DataSchemaExtB {
3729                        m: B("d".to_string())
3730                    }),
3731                    attype: Default::default(),
3732                    title: Default::default(),
3733                    titles: Default::default(),
3734                    description: Default::default(),
3735                    descriptions: Default::default(),
3736                    constant: Default::default(),
3737                    default: Default::default(),
3738                    unit: Default::default(),
3739                    one_of: Default::default(),
3740                    enumeration: Default::default(),
3741                    read_only: Default::default(),
3742                    write_only: Default::default(),
3743                    format: Default::default(),
3744                }),
3745                other: Nil::cons(ActionAffordanceExtA { b: A(3) }).cons(ActionAffordanceExtB {
3746                    i: B("c".to_string())
3747                }),
3748                output: Default::default(),
3749                safe: Default::default(),
3750                idempotent: Default::default(),
3751                synchronous: Default::default(),
3752            },
3753        );
3754    }
3755
3756    #[test]
3757    fn build_invalid_property_affordance() {
3758        let builder = PropertyAffordanceBuilder::<
3759            Nil,
3760            PartialDataSchemaBuilder<_, _, _, _>,
3761            (),
3762            (),
3763        >::default()
3764        .number()
3765        .titles(|b| b.add("i1t", "title1"))
3766        .into_usable();
3767
3768        assert_eq!(
3769            builder.build().unwrap_err(),
3770            Error::InvalidLanguageTag("i1t".to_string()),
3771        );
3772    }
3773
3774    #[test]
3775    fn build_invalid_action_affordance() {
3776        let builder = ActionAffordanceBuilder::<Nil, (), ()>::default()
3777            .titles(|b| b.add("i1t", "title1"))
3778            .into_usable();
3779
3780        assert_eq!(
3781            builder.build().unwrap_err(),
3782            Error::InvalidLanguageTag("i1t".to_string()),
3783        );
3784    }
3785
3786    #[test]
3787    fn build_invalid_event_affordance() {
3788        let builder = EventAffordanceBuilder::<Nil, (), ()>::default()
3789            .titles(|b| b.add("i1t", "title1"))
3790            .into_usable();
3791
3792        assert_eq!(
3793            builder.build().unwrap_err(),
3794            Error::InvalidLanguageTag("i1t".to_string()),
3795        );
3796    }
3797
3798    #[test]
3799    fn delegate_vec_methods() {
3800        let builder  = PropertyAffordanceBuilder::<Nil, PartialDataSchemaBuilder<_, _, _, _>, (), ()>::default()
3801            .vec()
3802            .read_only()
3803            .min_items(3)
3804            .max_items(5)
3805            .set_item(|b| b.finish_extend().integer())
3806            .into_usable();
3807
3808        assert_eq!(
3809            builder.build().unwrap(),
3810            PropertyAffordance {
3811                interaction: Default::default(),
3812                data_schema: DataSchema {
3813                    read_only: true,
3814                    subtype: Some(DataSchemaSubtype::Array(ArraySchema {
3815                        items: Some(BoxedElemOrVec::Elem(Box::new(DataSchema {
3816                            subtype: Some(DataSchemaSubtype::Integer(Default::default())),
3817                            other: Nil,
3818                            ..Default::default()
3819                        }))),
3820                        min_items: Some(3),
3821                        max_items: Some(5),
3822                        other: Nil,
3823                    })),
3824                    other: Nil,
3825                    ..Default::default()
3826                },
3827                observable: Default::default(),
3828                other: Nil,
3829            }
3830        )
3831    }
3832
3833    #[test]
3834    fn delegate_tuple_methods() {
3835        let builder  = PropertyAffordanceBuilder::<Nil, PartialDataSchemaBuilder<_, _, _, _>, (), ()>::default()
3836            .tuple()
3837            .read_only()
3838            .append(|b| b.finish_extend().integer())
3839            .append(|b| b.finish_extend().string())
3840            .into_usable();
3841
3842        assert_eq!(
3843            builder.build().unwrap(),
3844            PropertyAffordance {
3845                interaction: Default::default(),
3846                data_schema: DataSchema {
3847                    read_only: true,
3848                    subtype: Some(DataSchemaSubtype::Array(ArraySchema {
3849                        items: Some(BoxedElemOrVec::Vec(vec![
3850                            DataSchema {
3851                                subtype: Some(DataSchemaSubtype::Integer(Default::default())),
3852                                other: Nil,
3853                                ..Default::default()
3854                            },
3855                            DataSchema {
3856                                subtype: Some(DataSchemaSubtype::String(Default::default())),
3857                                other: Nil,
3858                                ..Default::default()
3859                            },
3860                        ])),
3861                        min_items: None,
3862                        max_items: None,
3863                        other: Nil,
3864                    })),
3865                    other: Nil,
3866                    ..Default::default()
3867                },
3868                observable: Default::default(),
3869                other: Nil,
3870            }
3871        )
3872    }
3873}