1pub use std::collections::HashMap;
7pub use crate::api::*;
8use crate::errors::*;
9use serde::{Deserialize, Serialize};
10use serde_json::{from_value, to_value, Value, Map};
11
12pub trait JsonApiModel: Serialize
18where
19 for<'de> Self: Deserialize<'de>,
20{
21 #[doc(hidden)]
22 fn jsonapi_type(&self) -> String;
23 #[doc(hidden)]
24 fn jsonapi_id(&self) -> String;
25 #[doc(hidden)]
26 fn relationship_fields() -> Option<&'static [&'static str]>;
27 #[doc(hidden)]
28 fn build_relationships(&self) -> Option<Relationships>;
29 #[doc(hidden)]
30 fn build_included(&self) -> Option<Resources>;
31
32 fn from_jsonapi_resource(resource: &Resource, included: &Option<Resources>)
33 -> Result<Self>
34 {
35
36 let visited_relationships: Vec<&str> = Vec::new();
37 Self::from_serializable(Self::resource_to_attrs(resource, included, &visited_relationships))
38 }
39
40 fn from_jsonapi_document(doc: &DocumentData) -> Result<Self> {
46 match doc.data.as_ref() {
47 Some(primary_data) => {
48 match *primary_data {
49 PrimaryData::None => bail!("Document had no data"),
50 PrimaryData::Single(ref resource) => {
51 Self::from_jsonapi_resource(resource, &doc.included)
52 }
53 PrimaryData::Multiple(ref resources) => {
54 let visited_relationships: Vec<&str> = Vec::new();
55 let all: Vec<ResourceAttributes> = resources
56 .iter()
57 .map(|r| Self::resource_to_attrs(r, &doc.included, &visited_relationships))
58 .collect();
59 Self::from_serializable(all)
60 }
61 }
62 }
63 None => bail!("Document had no data"),
64 }
65 }
66
67 fn to_jsonapi_resource(&self) -> (Resource, Option<Resources>) {
70 if let Value::Object(mut attrs) = to_value(self).unwrap() {
71 let _ = attrs.remove("id");
72 let resource = Resource {
73 _type: self.jsonapi_type(),
74 id: self.jsonapi_id(),
75 relationships: self.build_relationships(),
76 attributes: Self::extract_attributes(&attrs),
77 ..Default::default()
78 };
79
80 (resource, self.build_included())
81 } else {
82 panic!("{} is not a Value::Object", self.jsonapi_type())
83 }
84 }
85
86
87 fn to_jsonapi_document(&self) -> JsonApiDocument {
90 let (resource, included) = self.to_jsonapi_resource();
91 JsonApiDocument::Data (
92 DocumentData {
93 data: Some(PrimaryData::Single(Box::new(resource))),
94 included,
95 ..Default::default()
96 }
97 )
98 }
99
100
101 #[doc(hidden)]
102 fn build_has_one<M: JsonApiModel>(model: &M) -> Relationship {
103 Relationship {
104 data: Some(IdentifierData::Single(model.as_resource_identifier())),
105 links: None
106 }
107 }
108
109 #[doc(hidden)]
110 fn build_has_many<M: JsonApiModel>(models: &[M]) -> Relationship {
111 Relationship {
112 data: Some(IdentifierData::Multiple(
113 models.iter().map(|m| m.as_resource_identifier()).collect()
114 )),
115 links: None
116 }
117 }
118
119 #[doc(hidden)]
120 fn as_resource_identifier(&self) -> ResourceIdentifier {
121 ResourceIdentifier {
122 _type: self.jsonapi_type(),
123 id: self.jsonapi_id(),
124 }
125 }
126
127 #[doc(hidden)]
132 fn extract_attributes(attrs: &Map<String, Value>) -> ResourceAttributes {
133 attrs
134 .iter()
135 .filter(|&(key, _)| {
136 if let Some(fields) = Self::relationship_fields() {
137 if fields.contains(&key.as_str()) {
138 return false;
139 }
140 }
141 true
142 })
143 .map(|(k, v)| (k.clone(), v.clone()))
144 .collect()
145 }
146
147 #[doc(hidden)]
148 fn to_resources(&self) -> Resources {
149 let (me, maybe_others) = self.to_jsonapi_resource();
150 let mut flattened = vec![me];
151 if let Some(mut others) = maybe_others {
152 flattened.append(&mut others);
153 }
154 flattened
155 }
156
157 #[doc(hidden)]
162 fn lookup<'a>(needle: &ResourceIdentifier, haystack: &'a [Resource])
163 -> Option<&'a Resource>
164 {
165 for resource in haystack {
166 if resource._type == needle._type && resource.id == needle.id {
167 return Some(resource);
168 }
169 }
170 None
171 }
172
173 #[doc(hidden)]
192 fn resource_to_attrs(resource: &Resource, included: &Option<Resources>, visited_relationships: &Vec<&str>)
193 -> ResourceAttributes
194 {
195 let mut new_attrs = HashMap::new();
196 new_attrs.clone_from(&resource.attributes);
197 new_attrs.insert("id".into(), resource.id.clone().into());
198
199 let mut this_visited: Vec<&str> = Vec::new();
203 for rel in visited_relationships.iter() {
204 this_visited.push(rel);
205 }
206
207 if let Some(relations) = resource.relationships.as_ref() {
208 if let Some(inc) = included.as_ref() {
209 for (name, relation) in relations {
210 if this_visited.contains(&name.as_str()) {
213 return new_attrs;
214 }
215 this_visited.push(name);
217
218 let value = match relation.data {
219 Some(IdentifierData::None) => Value::Null,
220 Some(IdentifierData::Single(ref identifier)) => {
221 let found = Self::lookup(identifier, inc)
222 .map(|r| Self::resource_to_attrs(r, included, &this_visited) );
223 to_value(found)
224 .expect("Casting Single relation to value")
225 },
226 Some(IdentifierData::Multiple(ref identifiers)) => {
227 let found: Vec<Option<ResourceAttributes>> =
228 identifiers.iter().map(|identifier|{
229 Self::lookup(identifier, inc).map(|r|{
230 Self::resource_to_attrs(r, included, &this_visited)
231 })
232 }).collect();
233 to_value(found)
234 .expect("Casting Multiple relation to value")
235 },
236 None => Value::Null,
237 };
238 new_attrs.insert(name.to_string(), value);
239 }
240 }
241 }
242 new_attrs
243 }
244
245 #[doc(hidden)]
246 fn from_serializable<S: Serialize>(s: S) -> Result<Self> {
247 from_value(to_value(s)?).map_err(Error::from)
248 }
249}
250
251pub fn vec_to_jsonapi_resources<T: JsonApiModel>(
255 objects: Vec<T>,
256) -> (Resources, Option<Resources>) {
257 let mut included = vec![];
258 let resources = objects
259 .iter()
260 .map(|obj| {
261 let (res, mut opt_incl) = obj.to_jsonapi_resource();
262 if let Some(ref mut incl) = opt_incl {
263 included.append(incl);
264 }
265 res
266 })
267 .collect::<Vec<_>>();
268 let opt_included = if included.is_empty() {
269 None
270 } else {
271 Some(included)
272 };
273 (resources, opt_included)
274}
275
276pub fn vec_to_jsonapi_document<T: JsonApiModel>(objects: Vec<T>) -> JsonApiDocument {
307 let (resources, included) = vec_to_jsonapi_resources(objects);
308 JsonApiDocument::Data (
309 DocumentData {
310 data: Some(PrimaryData::Multiple(resources)),
311 included,
312 ..Default::default()
313 }
314 )
315}
316
317impl<M: JsonApiModel> JsonApiModel for Box<M> {
318 fn jsonapi_type(&self) -> String {
319 self.as_ref().jsonapi_type()
320 }
321
322 fn jsonapi_id(&self) -> String {
323 self.as_ref().jsonapi_id()
324 }
325
326 fn relationship_fields() -> Option<&'static [&'static str]> {
327 M::relationship_fields()
328 }
329
330 fn build_relationships(&self) -> Option<Relationships> {
331 self.as_ref().build_relationships()
332 }
333
334 fn build_included(&self) -> Option<Resources> {
335 self.as_ref().build_included()
336 }
337}
338
339#[macro_export]
343macro_rules! jsonapi_model {
344 ($model:ty; $type:expr) => (
345 impl JsonApiModel for $model {
346 fn jsonapi_type(&self) -> String { $type.to_string() }
347 fn jsonapi_id(&self) -> String { self.id.to_string() }
348 fn relationship_fields() -> Option<&'static [&'static str]> { None }
349 fn build_relationships(&self) -> Option<Relationships> { None }
350 fn build_included(&self) -> Option<Resources> { None }
351 }
352 );
353 ($model:ty; $type:expr;
354 has one $( $has_one:ident ),*
355 ) => (
356 jsonapi_model!($model; $type; has one $( $has_one ),*; has many);
357 );
358 ($model:ty; $type:expr;
359 has many $( $has_many:ident ),*
360 ) => (
361 jsonapi_model!($model; $type; has one; has many $( $has_many ),*);
362 );
363 ($model:ty; $type:expr;
364 has one $( $has_one:ident ),*;
365 has many $( $has_many:ident ),*
366 ) => (
367 impl JsonApiModel for $model {
368 fn jsonapi_type(&self) -> String { $type.to_string() }
369 fn jsonapi_id(&self) -> String { self.id.to_string() }
370
371 fn relationship_fields() -> Option<&'static [&'static str]> {
372 static FIELDS: &'static [&'static str] = &[
373 $( stringify!($has_one),)*
374 $( stringify!($has_many),)*
375 ];
376
377 Some(FIELDS)
378 }
379
380 fn build_relationships(&self) -> Option<Relationships> {
381 let mut relationships = HashMap::new();
382 $(
383 relationships.insert(stringify!($has_one).into(),
384 Self::build_has_one(&self.$has_one)
385 );
386 )*
387 $(
388 relationships.insert(
389 stringify!($has_many).into(),
390 {
391 let values = &self.$has_many.get_models();
392 Self::build_has_many(values)
393 }
394 );
395 )*
396 Some(relationships)
397 }
398
399 fn build_included(&self) -> Option<Resources> {
400 let mut included:Resources = vec![];
401 $( included.append(&mut self.$has_one.to_resources()); )*
402 $(
403 for model in self.$has_many.get_models() {
404 included.append(&mut model.to_resources());
405 }
406 )*
407 Some(included)
408 }
409 }
410 );
411}