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 {}