1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Structured extensions for building and parsing Thing Descriptions
//!
//! The Thing Description can be extended with additional ontologies using its JSON-LD [@context](https://www.w3.org/TR/json-ld11/#the-context).
//!
//! This module provides a trait, [ExtendableThing], to define extensions for each of the standard
//! elements of a description.

use serde::{Deserialize, Serialize};

use crate::hlist::{Cons, Nil};

/// Requirement trait for extending a Thing Description element
pub trait ExtendablePiece: Serialize + for<'a> Deserialize<'a> {}

impl<T> ExtendablePiece for T where T: Serialize + for<'a> Deserialize<'a> {}

/// Main extension trait
///
/// The trait uses an associated type for each element of the ThingDescription, set it to `()` if
/// the extension does not apply to that specific element.
pub trait ExtendableThing {
    /// The extension type for [`InteractionAffordance`].
    ///
    /// [`InteractionAffordance`]: crate::thing::InteractionAffordance
    type InteractionAffordance: ExtendablePiece;

    /// The extension type for [`PropertyAffordance`].
    ///
    /// [`PropertyAffordance`]: crate::thing::PropertyAffordance
    type PropertyAffordance: ExtendablePiece;

    /// The extension type for [`ActionAffordance`].
    ///
    /// [`ActionAffordance`]: crate::thing::ActionAffordance
    type ActionAffordance: ExtendablePiece;

    /// The extension type for [`EventAffordance`].
    ///
    /// [`EventAffordance`]: crate::thing::EventAffordance
    type EventAffordance: ExtendablePiece;

    /// The extension type for [`Form`].
    ///
    /// [`Form`]: crate::thing::Form
    type Form: ExtendablePiece;

    /// The extension type for [`ExpectedResponse`].
    ///
    /// [`ExpectedResponse`]: crate::thing::ExpectedResponse
    type ExpectedResponse: ExtendablePiece;

    /// The extension type for [`DataSchema`].
    ///
    /// [`DataSchema`]: crate::thing::DataSchema
    type DataSchema: ExtendablePiece;

    /// The extension type for [`ObjectSchema`].
    ///
    /// [`ObjectSchema`]: crate::thing::ObjectSchema
    type ObjectSchema: ExtendablePiece;

    /// The extension type for [`ArraySchema`].
    ///
    /// [`ArraySchema`]: crate::thing::ArraySchema
    type ArraySchema: ExtendablePiece;
}

impl ExtendableThing for Nil {
    type InteractionAffordance = Nil;
    type PropertyAffordance = Nil;
    type ActionAffordance = Nil;
    type EventAffordance = Nil;
    type Form = Nil;
    type ExpectedResponse = Nil;
    type DataSchema = Nil;
    type ObjectSchema = Nil;
    type ArraySchema = Nil;
}

impl<T, U> ExtendableThing for Cons<T, U>
where
    T: ExtendableThing,
    U: ExtendableThing,
{
    type InteractionAffordance = Cons<T::InteractionAffordance, U::InteractionAffordance>;
    type PropertyAffordance = Cons<T::PropertyAffordance, U::PropertyAffordance>;
    type ActionAffordance = Cons<T::ActionAffordance, U::ActionAffordance>;
    type EventAffordance = Cons<T::EventAffordance, U::EventAffordance>;
    type Form = Cons<T::Form, U::Form>;
    type ExpectedResponse = Cons<T::ExpectedResponse, U::ExpectedResponse>;
    type DataSchema = Cons<T::DataSchema, U::DataSchema>;
    type ObjectSchema = Cons<T::ObjectSchema, U::ObjectSchema>;
    type ArraySchema = Cons<T::ArraySchema, U::ArraySchema>;
}

/// A trait representing an object that can be created empty in order to extend a `Thing`.
///
/// This is separated from the [`Extend`] trait because it is not generic and it only contains an
/// associate type.
pub trait Extendable {
    /// The empty extension type.
    type Empty;

    /// Create an empty extension
    fn empty() -> Self::Empty;
}

/// A generic trait to express an object to extend a `Thing`.
///
/// This trait represents an object that can be _extended_ with other typed expressions. It is used
/// extensively for all the _extendable_ types of a `Thing`.
///
/// The generic type `T` is the type that can be _added_ to the implemented `struct`/`enum`.
///
/// The trait is generally used in combination with the [`Extendable`] trait.
pub trait Extend<T>: Sized {
    /// The new type obtained when extending `Self`.
    type Target;

    /// Extend the current extension with an additional element
    fn ext(self, t: T) -> Self::Target;

    /// Extends the current type, passing a closure that returns `T`.
    fn ext_with<F>(self, f: F) -> Self::Target
    where
        F: FnOnce() -> T,
    {
        self.ext(f())
    }
}

impl Extendable for Nil {
    type Empty = Nil;

    fn empty() -> Self {
        Nil
    }
}

impl<T> Extend<T> for Nil {
    type Target = Cons<T, Nil>;

    fn ext(self, t: T) -> Self::Target {
        Nil::cons(t)
    }
}

impl<T, U> Extendable for Cons<T, U> {
    type Empty = Nil;

    fn empty() -> Self::Empty {
        Nil
    }
}

impl<T, U, V> Extend<T> for Cons<U, V> {
    type Target = Cons<T, Cons<U, V>>;

    fn ext(self, t: T) -> Self::Target {
        self.cons(t)
    }
}