json_api/doc/
object.rs

1use std::cmp::{Eq, PartialEq};
2use std::hash::{Hash, Hasher};
3use std::mem;
4
5use doc::{Data, Document, Identifier, Link, PrimaryData, Relationship};
6use error::Error;
7use query::Query;
8use sealed::Sealed;
9use value::{Key, Map, Set, Value};
10use view::Render;
11
12/// A preexisting resource. Commonly found in the document of a response or `PATCH`
13/// request.
14///
15/// Both the [`id`] and [`type`][`kind`] field must be present if an `Object` is
16/// deserialized. If you need to represent a resource object that does not already have
17/// an [`id`], you can use [`NewObject`]. For more information, check out the *[resource
18/// objects]* section of the JSON API specification.
19///
20/// # Equality
21///
22/// Objects are considered to be equal if they have the same [`id`] and [`kind`].
23///
24/// ```
25/// # extern crate json_api;
26/// #
27/// # use json_api::Error;
28/// #
29/// # fn example() -> Result<(), Error> {
30/// use json_api::doc::Object;
31/// use json_api::value::Key;
32///
33/// let person = "person".parse::<Key>()?;
34/// let hero = "hero".parse::<Key>()?;
35///
36/// let mut bruce = Object::new(person.clone(), "🦇".to_owned());
37/// let mut batman = Object::new(person.clone(), "🦇".to_owned());
38///
39/// bruce.attributes.insert("name".parse()?, "Bruce Wayne".into());
40/// batman.attributes.insert("name".parse()?, "Batman".into());
41///
42/// // Bruce and Batman are equal because they have the same `id` and `kind`.
43/// assert!(bruce == batman);
44///
45/// // Let's make Batman a "hero" instead of a "person".
46/// batman.kind = hero.clone();
47///
48/// // Bruce and Batman are no longer equal.
49/// assert!(bruce != batman);
50/// #
51/// # Ok(())
52/// # }
53/// #
54/// # fn main() {
55/// # example().unwrap();
56/// # }
57/// ```
58///
59/// Since an [`Identifier`] is a subset of `Object` with fields necessary for
60/// identification, you can compare the two.
61///
62/// ```
63/// # extern crate json_api;
64/// #
65/// # use json_api::Error;
66/// #
67/// # fn example() -> Result<(), Error> {
68/// # use json_api::doc::Object;
69/// # use json_api::value::Key;
70/// #
71/// # let hero = "hero".parse::<Key>()?;
72/// # let batman = Object::new(hero.clone(), "🦇".to_owned());
73/// #
74/// use json_api::doc::Identifier;
75/// assert!(Identifier::from(&batman) == batman);
76/// #
77/// # Ok(())
78/// # }
79/// #
80/// # fn main() {
81/// # example().unwrap();
82/// # }
83/// ```
84///
85/// # Hashing
86///
87/// Similar to how equality works, object's are hashed by their [`id`] and [`kind`]. This
88/// allows for easy and efficient deduplication when embedding related resources in a
89/// response.
90///
91/// **Note:** The following example is to demonstrate how object's are hashed.
92/// Deduplication occurs automatically if you use the [`json_api::to_doc`] function with
93/// a [`Resource`] that was implemented with the [`resource!`] macro.
94///
95/// ```
96/// # extern crate json_api;
97/// #
98/// # use json_api::Error;
99/// #
100/// # fn example() -> Result<(), Error> {
101/// use json_api::doc::Object;
102/// use json_api::value::{Key, Set};
103///
104/// let person = "person".parse::<Key>()?;
105/// let hero = "hero".parse::<Key>()?;
106///
107/// let mut included = Set::new();
108///
109/// let mut diana = Object::new(person.clone(), "🛡".to_owned());
110/// let mut wonder_woman = Object::new(person.clone(), "🛡".to_owned());
111///
112/// diana.attributes.insert("name".parse()?, "Diana Prince".into());
113/// wonder_woman.attributes.insert("name".parse()?, "Wonder Woman".into());
114///
115/// included.insert(diana);
116/// assert_eq!(included.len(), 1);
117///
118/// included.insert(wonder_woman.clone());
119/// assert_eq!(included.len(), 1);
120///
121/// // Let's update Wonder Woman's kind to "hero" so we can embed her in the response.
122/// wonder_woman.kind = hero.clone();
123///
124/// included.insert(wonder_woman.clone());
125/// assert_eq!(included.len(), 2);
126/// #
127/// # Ok(())
128/// # }
129/// #
130/// # fn main() {
131/// # example().unwrap();
132/// # }
133/// ```
134///
135/// [`Identifier`]: ./struct.Identifier.html
136/// [`NewObject`]: ./struct.NewObject.html
137/// [`Resource`]: ../trait.Resource.html
138/// [`resource!`]: ../macro.resource.html
139/// [`json_api::to_doc`]: ../fn.to_doc.html
140/// [`id`]: #structfield.id
141/// [`kind`]: #structfield.kind
142/// [resource objects]: https://goo.gl/55cSP7
143#[derive(Clone, Debug, Deserialize, Serialize)]
144pub struct Object {
145    /// Contains some of the object's data. If this value of this field is empty, it will
146    /// not be serialized. For more information, check out the *[attributes]* section of
147    /// the JSON API specification.
148    ///
149    /// [attributes]: https://goo.gl/TshgH1
150    #[serde(default, skip_serializing_if = "Map::is_empty")]
151    pub attributes: Map,
152
153    /// A string that contains a unique identfier for this resource type (`kind`). For
154    /// more information, check out the *[identification]* section of the JSON API
155    /// specification.
156    ///
157    /// [identification]: https://goo.gl/3s681i
158    pub id: String,
159
160    /// Describes resources that share common attributes and relationships. This field is
161    /// derived from the `type` field if the object is deserialized. For more
162    /// information, check out the *[identification]* section of the JSON API
163    /// specification.
164    ///
165    /// [identification]: https://goo.gl/3s681i
166    #[serde(rename = "type")]
167    pub kind: Key,
168
169    /// Contains relevant links. If this value of this field is empty, it will not be
170    /// serialized. For more information, check out the *[links]* section of the JSON
171    /// API specification.
172    ///
173    /// [links]: https://goo.gl/E4E6Vt
174    #[serde(default, skip_serializing_if = "Map::is_empty")]
175    pub links: Map<Key, Link>,
176
177    /// Non-standard meta information. If this value of this field is empty, it will not
178    /// be serialized. For more information, check out the *[meta information]* section
179    /// of the JSON API specification.
180    ///
181    /// [meta information]: https://goo.gl/LyrGF8
182    #[serde(default, skip_serializing_if = "Map::is_empty")]
183    pub meta: Map,
184
185    /// Describes relationships between this object and other resource objects. If this
186    /// value of this field is empty, it will not be serialized. For more information,
187    /// check out the *[relationships]* section of the JSON API specification.
188    ///
189    /// [relationships]: https://goo.gl/Gxghwc
190    #[serde(default, skip_serializing_if = "Map::is_empty")]
191    pub relationships: Map<Key, Relationship>,
192
193    /// Private field for backwards compatibility.
194    #[serde(skip)]
195    _ext: (),
196}
197
198impl Object {
199    /// Returns a new `Object`.
200    ///
201    /// # Example
202    ///
203    /// ```
204    /// # extern crate json_api;
205    /// #
206    /// # use json_api::Error;
207    /// #
208    /// # fn example() -> Result<(), Error> {
209    /// use json_api::doc::Object;
210    /// let mut obj = Object::new("users".parse()?, "1".to_owned());
211    /// # Ok(())
212    /// # }
213    /// #
214    /// # fn main() {
215    /// # example().unwrap();
216    /// # }
217    /// ```
218    pub fn new(kind: Key, id: String) -> Self {
219        Object {
220            id,
221            kind,
222            attributes: Default::default(),
223            links: Default::default(),
224            meta: Default::default(),
225            relationships: Default::default(),
226            _ext: (),
227        }
228    }
229}
230
231impl Eq for Object {}
232
233impl Hash for Object {
234    fn hash<H: Hasher>(&self, state: &mut H) {
235        self.id.hash(state);
236        self.kind.hash(state);
237    }
238}
239
240impl PartialEq for Object {
241    fn eq(&self, rhs: &Object) -> bool {
242        self.id == rhs.id && self.kind == rhs.kind
243    }
244}
245
246impl PartialEq<Identifier> for Object {
247    fn eq(&self, rhs: &Identifier) -> bool {
248        self.id == rhs.id && self.kind == rhs.kind
249    }
250}
251
252impl Render<Identifier> for Object {
253    fn render(self, query: Option<&Query>) -> Result<Document<Identifier>, Error> {
254        Identifier::from(self).render(query)
255    }
256}
257
258impl Render<Identifier> for Vec<Object> {
259    fn render(self, _: Option<&Query>) -> Result<Document<Identifier>, Error> {
260        let data = self.into_iter().map(Identifier::from).collect();
261
262        Ok(Document::Ok {
263            data,
264            included: Default::default(),
265            jsonapi: Default::default(),
266            links: Default::default(),
267            meta: Default::default(),
268        })
269    }
270}
271
272impl Render<Object> for Object {
273    fn render(mut self, _: Option<&Query>) -> Result<Document<Object>, Error> {
274        let links = mem::replace(&mut self.links, Default::default());
275        let meta = mem::replace(&mut self.meta, Default::default());
276
277        Ok(Document::Ok {
278            links,
279            meta,
280            data: Data::Member(Box::new(Some(self))),
281            included: Default::default(),
282            jsonapi: Default::default(),
283        })
284    }
285}
286
287impl Render<Object> for Vec<Object> {
288    fn render(self, _: Option<&Query>) -> Result<Document<Object>, Error> {
289        Ok(Document::Ok {
290            data: Data::Collection(self),
291            included: Default::default(),
292            jsonapi: Default::default(),
293            links: Default::default(),
294            meta: Default::default(),
295        })
296    }
297}
298
299impl PrimaryData for Object {
300    fn flatten(self, incl: &Set<Object>) -> Value {
301        #[cfg_attr(rustfmt, rustfmt_skip)]
302        let Object { id, attributes, relationships, .. } = self;
303        let mut map = {
304            let size = attributes.len() + relationships.len() + 1;
305            Map::with_capacity(size)
306        };
307
308        map.insert(Key::from_raw("id".to_owned()), Value::String(id));
309        map.extend(attributes);
310
311        for (key, value) in relationships {
312            let value = match value.data {
313                Data::Member(data) => match *data {
314                    Some(item) => item.flatten(incl),
315                    None => Value::Null,
316                },
317                Data::Collection(data) => {
318                    let iter = data.into_iter().map(|item| item.flatten(incl));
319                    Value::Array(iter.collect())
320                }
321            };
322
323            map.insert(key, value);
324        }
325
326        Value::Object(map)
327    }
328}
329
330impl Sealed for Object {}
331
332/// A resource that does not already exist. Commonly found in the document of a
333/// `POST` request.
334///
335/// For more information, check out the *[creating resources]* section of the JSON API
336/// specification.
337///
338/// [creating resources]: https://goo.gl/KoLQgh
339#[derive(Clone, Debug, Deserialize, Serialize)]
340pub struct NewObject {
341    /// Contains some of the object's data. If this value of this field is empty, it will
342    /// not be serialized. For more information, check out the *[attributes]* section of
343    /// the JSON API specification.
344    ///
345    /// [attributes]: https://goo.gl/TshgH1
346    #[serde(default, skip_serializing_if = "Map::is_empty")]
347    pub attributes: Map,
348
349    /// An optional string that contains a unique identfier for this resource type
350    /// (`kind`). A `Some` value here should be interpreted as *[client-generated id]*.
351    /// For more information, check out the *[identification]* section of
352    /// the JSON API specification.
353    ///
354    /// [client-generated id]: https://goo.gl/W16smj
355    /// [identification]: https://goo.gl/3s681i
356    pub id: Option<String>,
357
358    /// Describes resources that share common attributes and relationships. This field
359    /// is derived from the `type` field if the object is deserialized. For more
360    /// information, check out the *[identification]* section of the JSON API
361    /// specification.
362    ///
363    /// [identification]: https://goo.gl/3s681i
364    #[serde(rename = "type")]
365    pub kind: Key,
366
367    /// Contains relevant links. If this value of this field is empty, it will not be
368    /// serialized. For more information, check out the *[links]* section of the JSON
369    /// API specification.
370    ///
371    /// [links]: https://goo.gl/E4E6Vt
372    #[serde(default, skip_serializing_if = "Map::is_empty")]
373    pub links: Map<Key, Link>,
374
375    /// Non-standard meta information. If this value of this field is empty, it will not
376    /// be serialized. For more information, check out the *[meta information]* section
377    /// of the JSON API specification.
378    ///
379    /// [meta information]: https://goo.gl/LyrGF8
380    #[serde(default, skip_serializing_if = "Map::is_empty")]
381    pub meta: Map,
382
383    /// Describes relationships between this object and other resource objects. If this
384    /// value of this field is empty, it will not be serialized. For more information,
385    /// check out the *[relationships]* section of the JSON API specification.
386    ///
387    /// [relationships]: https://goo.gl/Gxghwc
388    #[serde(default, skip_serializing_if = "Map::is_empty")]
389    pub relationships: Map<Key, Relationship>,
390
391    /// Private field for backwards compatibility.
392    #[serde(skip)]
393    _ext: (),
394}
395
396impl NewObject {
397    /// Returns a new `NewObject`.
398    ///
399    /// # Example
400    ///
401    /// ```
402    /// # extern crate json_api;
403    /// #
404    /// # use json_api::Error;
405    /// #
406    /// # fn example() -> Result<(), Error> {
407    /// use json_api::doc::NewObject;
408    /// let mut obj = NewObject::new("users".parse()?);
409    /// # Ok(())
410    /// # }
411    /// #
412    /// # fn main() {
413    /// # example().unwrap();
414    /// # }
415    /// ```
416    pub fn new(kind: Key) -> Self {
417        NewObject {
418            kind,
419            id: Default::default(),
420            attributes: Default::default(),
421            links: Default::default(),
422            meta: Default::default(),
423            relationships: Default::default(),
424            _ext: (),
425        }
426    }
427}
428
429impl PrimaryData for NewObject {
430    fn flatten(self, _: &Set<Object>) -> Value {
431        #[cfg_attr(rustfmt, rustfmt_skip)]
432        let NewObject { id, attributes, relationships, .. } = self;
433        let mut map = {
434            let size = attributes.len() + relationships.len() + 1;
435            Map::with_capacity(size)
436        };
437
438        if let Some(value) = id {
439            map.insert(Key::from_raw("id".to_owned()), Value::String(value));
440        }
441
442        map.extend(attributes);
443
444        for (key, value) in relationships {
445            let value = match value.data {
446                Data::Member(data) => match *data {
447                    Some(Identifier { id, .. }) => Value::String(id),
448                    None => Value::Null,
449                },
450                Data::Collection(data) => {
451                    data.into_iter().map(|ident| ident.id).collect()
452                }
453            };
454
455            map.insert(key, value);
456        }
457
458        Value::Object(map)
459    }
460}
461
462impl Render<NewObject> for NewObject {
463    fn render(self, _: Option<&Query>) -> Result<Document<NewObject>, Error> {
464        Ok(Document::Ok {
465            data: Data::Member(Box::new(Some(self))),
466            included: Default::default(),
467            jsonapi: Default::default(),
468            links: Default::default(),
469            meta: Default::default(),
470        })
471    }
472}
473
474impl Sealed for NewObject {}