siren_types/
types.rs

1use serde::{Deserialize, Serialize};
2use std::error::Error;
3use std::fmt;
4
5#[derive(Debug, Serialize, Deserialize)]
6pub struct Entity {
7    /// Describes the nature of an entity's content based on the current
8    /// representation. Possible values are implementation-dependent and should
9    /// be documented. MUST be an array of strings. Optional.
10    #[serde(default)]
11    pub class: Vec<String>,
12    /// A set of key-value pairs that describe the state of an entity. In JSON
13    /// Siren, this is an object such as { "name": "Kevin", "age": 30 }.
14    /// Optional.
15    #[serde(default)]
16    pub properties: serde_json::Value,
17    /// A collection of related sub-entities. If a sub-entity contains an href
18    /// value, it should be treated as an embedded link. Clients may choose to
19    /// optimistically load embedded links. If no href value exists, the
20    /// sub-entity is an embedded entity representation that contains all the
21    /// characteristics of a typical entity. One difference is that a sub-entity
22    /// MUST contain a rel attribute to describe its relationship to the parent
23    /// entity.
24    // In JSON Siren, this is represented as an array. Optional.
25    #[serde(default)]
26    pub entities: Vec<SubEntity>,
27    /// A collection of items that describe navigational links, distinct from
28    /// entity relationships. Link items should contain a rel attribute to
29    /// describe the relationship and an href attribute to point to the target
30    /// URI. Entities should include a link rel to self. In JSON Siren, this is
31    /// represented as "links": `[{ "rel": ["self"], "href": "http://api.x.io/orders/1234" }]`
32    /// Optional.
33    #[serde(default)]
34    pub links: Vec<NavigationalLink>,
35    /// A collection of action objects, represented in JSON Siren as an array
36    /// such as { "actions": [{ ... }] }. See Actions. Optional
37    #[serde(default)]
38    pub actions: Vec<Action>,
39    /// Descriptive text about the entity. Optional.
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub title: Option<String>,
42}
43
44impl Default for Entity {
45    fn default() -> Self {
46        Self {
47            class: Vec::default(),
48            properties: serde_json::Value::Object(serde_json::Map::default()),
49            entities: Vec::default(),
50            links: Vec::default(),
51            actions: Vec::default(),
52            title: None,
53        }
54    }
55}
56
57#[derive(Debug)]
58pub enum EntityBuilderError {
59    /// Whatever you passed, it doesn't serialize to a JSON object
60    NotAnObject,
61    Serde(serde_json::Error),
62}
63
64impl fmt::Display for EntityBuilderError {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        let desc = match self {
67            EntityBuilderError::NotAnObject => "does not serialize to an object",
68            EntityBuilderError::Serde(_) => "serialization failure",
69        };
70
71        write!(f, "{}", desc)
72    }
73}
74
75impl std::error::Error for EntityBuilderError {
76    fn source(&self) -> Option<&(dyn Error + 'static)> {
77        match self {
78            EntityBuilderError::NotAnObject => None,
79            EntityBuilderError::Serde(inner) => Some(inner),
80        }
81    }
82}
83
84impl From<serde_json::Error> for EntityBuilderError {
85    fn from(serde_error: serde_json::Error) -> Self {
86        EntityBuilderError::Serde(serde_error)
87    }
88}
89
90impl Entity {
91    pub fn with_properties<S: Serialize>(
92        self,
93        serializable: S,
94    ) -> Result<Self, EntityBuilderError> {
95        let value = serde_json::to_value(serializable)?;
96
97        match &value {
98            serde_json::Value::Object(_) => Ok(Entity {
99                properties: value,
100                ..self
101            }),
102            _ => Err(EntityBuilderError::NotAnObject),
103        }
104    }
105
106    pub fn with_class_member(mut self, class_member: impl Into<String>) -> Self {
107        self.class.push(class_member.into());
108
109        self
110    }
111
112    pub fn with_link(mut self, link: NavigationalLink) -> Self {
113        self.links.push(link);
114
115        self
116    }
117
118    pub fn with_action(mut self, action: Action) -> Self {
119        self.actions.push(action);
120
121        self
122    }
123
124    pub fn push_sub_entity(&mut self, sub_entity: SubEntity) {
125        self.entities.push(sub_entity);
126    }
127}
128
129#[derive(Debug, Serialize, Deserialize)]
130#[serde(untagged)]
131pub enum SubEntity {
132    Link {
133        #[serde(flatten)]
134        inner: EntityLink,
135    },
136    Embedded {
137        #[serde(flatten)]
138        inner: Entity,
139        /// Defines the relationship of the sub-entity to its parent, per Web
140        /// Linking (RFC5988) and Link Relations. MUST be a non-empty array of
141        /// strings. Required.
142        #[serde(default)]
143        rel: Vec<String>,
144    },
145}
146
147impl SubEntity {
148    pub fn from_link(entity_link: EntityLink) -> Self {
149        SubEntity::Link { inner: entity_link }
150    }
151
152    pub fn from_entity(entity: Entity, rels: &[impl Into<String> + Clone]) -> Self {
153        SubEntity::Embedded {
154            inner: entity,
155            rel: rels.iter().map(|rel| rel.clone().into()).collect(),
156        }
157    }
158}
159
160#[derive(Debug, Serialize, Deserialize)]
161pub struct EntityLink {
162    /// Describes the nature of an entity's content based on the current
163    /// representation. Possible values are implementation-dependent and
164    /// should be documented. MUST be an array of strings. Optional.
165    #[serde(default)]
166    pub class: Vec<String>,
167    /// Descriptive text about the entity. Optional.
168    #[serde(default, skip_serializing_if = "Option::is_none")]
169    pub title: Option<String>,
170    /// Defines the relationship of the sub-entity to its parent, per Web
171    /// Linking (RFC5988) and Link Relations. MUST be a non-empty array of
172    /// strings. Required.
173    #[serde(default)]
174    pub rel: Vec<String>,
175    /// The URI of the linked sub-entity. Required.
176    pub href: String,
177    /// Defines media type of the linked sub-entity, per Web Linking
178    /// (RFC5988). Optional.
179    #[serde(default, rename = "type", skip_serializing_if = "Option::is_none")]
180    pub _type: Option<String>,
181}
182
183#[derive(Debug, Serialize, Deserialize)]
184pub struct NavigationalLink {
185    /// Defines the relationship of the link to its entity, per Web Linking
186    /// (RFC5988) and Link Relations. MUST be an array of strings. Required.
187    pub rel: Vec<String>,
188    /// Describes aspects of the link based on the current representation.
189    /// Possible values are implementation-dependent and should be documented.
190    /// MUST be an array of strings. Optional.
191    #[serde(default)]
192    pub class: Vec<String>,
193    /// The URI of the linked resource. Required.
194    pub href: String,
195    /// Text describing the nature of a link. Optional.
196    #[serde(default, skip_serializing_if = "Option::is_none")]
197    pub title: Option<String>,
198    /// Defines media type of the linked resource, per Web Linking (RFC5988).
199    /// Optional.
200    #[serde(default, rename = "type", skip_serializing_if = "Option::is_none")]
201    pub _type: Option<String>,
202}
203
204impl NavigationalLink {
205    pub fn new(rels: &[impl Into<String> + Clone], href: impl Into<String>) -> Self {
206        Self {
207            href: href.into(),
208            rel: rels.iter().map(|rel| rel.clone().into()).collect(),
209            class: Vec::new(),
210            title: None,
211            _type: None,
212        }
213    }
214
215    pub fn with_class_member(mut self, class_member: impl Into<String>) -> Self {
216        self.class.push(class_member.into());
217
218        self
219    }
220
221    pub fn with_type(mut self, _type: impl Into<String>) -> Self {
222        self._type = Some(_type.into());
223
224        self
225    }
226
227    pub fn with_title(mut self, title: impl Into<String>) -> Self {
228        self.title = Some(title.into());
229
230        self
231    }
232}
233
234#[derive(Debug, Serialize, Deserialize)]
235pub struct Action {
236    /// A string that identifies the action to be performed. Action names MUST
237    /// be unique within the set of actions for an entity. The behaviour of
238    /// clients when parsing a Siren document that violates this constraint is
239    /// undefined. Required.
240    pub name: String,
241    /// Describes the nature of an action based on the current representation.
242    /// Possible values are implementation-dependent and should be documented.
243    /// MUST be an array of strings. Optional.
244    #[serde(default)]
245    pub class: Vec<String>,
246    /// An enumerated attribute mapping to a protocol method. For HTTP, these
247    /// values may be GET, PUT, POST, DELETE, or PATCH. As new methods are
248    /// introduced, this list can be extended. If this attribute is omitted, GET
249    /// should be assumed. Optional.
250    #[serde(with = "crate::http_serde::option_method")]
251    pub method: Option<http::Method>,
252    /// The URI of the action. Required.
253    pub href: String,
254    /// Descriptive text about the action. Optional.
255    #[serde(default, skip_serializing_if = "Option::is_none")]
256    pub title: Option<String>,
257    /// The encoding type for the request. When omitted and the fields attribute
258    /// exists, the default value is application/x-www-form-urlencoded.
259    /// Optional.
260    #[serde(default, rename = "type", skip_serializing_if = "Option::is_none")]
261    pub _type: Option<String>,
262    /// A collection of fields, expressed as an array of objects in JSON Siren
263    /// such as { "fields" : [{ ... }] }. See Fields. Optional.
264    #[serde(default)]
265    pub fields: Vec<Field>,
266}
267
268#[derive(Debug, Serialize, Deserialize)]
269pub struct Field {
270    /// A name describing the control. Field names MUST be unique within the set
271    /// of fields for an action. The behaviour of clients when parsing a Siren
272    /// document that violates this constraint is undefined. Required.
273    pub name: String,
274    /// Describes aspects of the field based on the current representation.
275    /// Possible values are implementation-dependent and should be documented.
276    /// MUST be an array of strings. Optional.
277    #[serde(default)]
278    pub class: Vec<String>,
279    /// The input type of the field. This may include any of the following input
280    /// types specified in HTML5:
281    /// hidden, text, search, tel, url, email, password, datetime, date, month,
282    /// week, time, datetime-local, number, range, color, checkbox, radio,
283    /// file
284    //
285    /// When missing, the default value is text. Serialization of these fields
286    /// will depend on the value of the action's type attribute. See type
287    /// under Actions, above. Optional.
288    #[serde(default, rename = "type", skip_serializing_if = "Option::is_none")]
289    pub _type: Option<String>,
290    /// A value assigned to the field. Optional.
291    #[serde(default, skip_serializing_if = "Option::is_none")]
292    pub value: Option<String>,
293    /// Textual annotation of a field. Clients may use this as a label.
294    /// Optional.
295    #[serde(default, skip_serializing_if = "Option::is_none")]
296    pub title: Option<String>,
297}