1use serde_json;
4use std::collections::HashMap;
5use crate::errors::*;
6use std::str::FromStr;
7use std;
8use serde::{Deserialize, Serialize};
9
10pub type JsonApiValue = serde_json::Value;
12
13pub type Resources = Vec<Resource>;
15pub type ResourceIdentifiers = Vec<ResourceIdentifier>;
17pub type Links = HashMap<String, JsonApiValue>;
18pub type Meta = HashMap<String, JsonApiValue>;
20pub type ResourceAttributes = HashMap<String, JsonApiValue>;
22pub type Relationships = HashMap<String, Relationship>;
24pub type Included = Vec<Resource>;
26pub type JsonApiErrors = Vec<JsonApiError>;
28
29pub type JsonApiId = String;
30pub type JsonApiIds<'a> = Vec<&'a JsonApiId>;
31
32#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
34pub struct ResourceIdentifier {
35 #[serde(rename = "type")]
36 pub _type: String,
37 pub id: JsonApiId,
38}
39
40#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
44pub struct Resource {
45 #[serde(rename = "type")]
46 pub _type: String,
47 pub id: JsonApiId,
48 #[serde(default)]
49 pub attributes: ResourceAttributes,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub relationships: Option<Relationships>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub links: Option<Links>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub meta: Option<Meta>,
56}
57
58#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
60pub struct Relationship {
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub data: Option<IdentifierData>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub links: Option<Links>,
65}
66
67#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
69#[serde(untagged)]
70pub enum PrimaryData {
71 None,
72 Single(Box<Resource>),
73 Multiple(Resources),
74}
75
76#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
78#[serde(untagged)]
79pub enum IdentifierData {
80 None,
81 Single(ResourceIdentifier),
82 Multiple(ResourceIdentifiers),
83}
84
85#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
87pub struct DocumentError {
88 pub errors: JsonApiErrors,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub links: Option<Links>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub meta: Option<Meta>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub jsonapi: Option<JsonApiInfo>,
95}
96
97#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
99pub struct DocumentData {
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub data: Option<PrimaryData>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub included: Option<Resources>,
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub links: Option<Links>,
106 #[serde(skip_serializing_if = "Option::is_none")]
107 pub meta: Option<Meta>,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub jsonapi: Option<JsonApiInfo>,
110}
111
112#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
116#[serde(untagged)]
117pub enum JsonApiDocument {
118 Error(DocumentError),
119 Data(DocumentData),
120}
121
122#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
124pub struct ErrorSource {
125 pub pointer: Option<String>,
126 pub parameter: Option<String>,
127}
128
129#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
131pub struct JsonApiError {
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub id: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub links: Option<Links>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub status: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub code: Option<String>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub title: Option<String>,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub detail: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub source: Option<ErrorSource>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub meta: Option<Meta>,
148}
149
150#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
153pub struct JsonApiInfo {
154 pub version: Option<String>,
155 pub meta: Option<Meta>,
156}
157
158#[derive(Serialize, Deserialize, Debug)]
160pub struct Pagination {
161 pub first: Option<String>,
162 pub prev: Option<String>,
163 pub next: Option<String>,
164 pub last: Option<String>,
165}
166
167
168#[derive(Debug)]
169pub struct Patch {
170 pub patch_type: PatchType,
171 pub subject: String,
172 pub previous: JsonApiValue,
173 pub next: JsonApiValue,
174}
175
176#[derive(Debug)]
177pub struct PatchSet {
178 pub resource_type: String,
179 pub resource_id: String,
180 pub patches: Vec<Patch>,
181}
182
183impl PatchSet {
184 pub fn new_for(resource: &Resource) -> Self {
185 PatchSet {
186 resource_type: resource._type.clone(),
187 resource_id: resource.id.clone(),
188 patches: Vec::<Patch>::new(),
189 }
190 }
191
192 pub fn push(&mut self, patch: Patch) {
193 self.patches.push(patch);
194 }
195}
196
197impl DocumentData {
198 fn has_meta(&self) -> bool {
199 self.meta.is_some()
200 }
201 fn has_included(&self) -> bool {
202 self.included.is_some()
203 }
204 fn has_data(&self) -> bool {
205 self.data.is_some()
206 }
207}
208
209impl JsonApiDocument {
212 pub fn is_valid(&self) -> bool {
219 self.validate().is_none()
220 }
221
222 pub fn validate(&self) -> Option<Vec<DocumentValidationError>> {
263 let mut errors = Vec::<DocumentValidationError>::new();
264
265 match self {
266 JsonApiDocument::Error(_x) => None,
267 JsonApiDocument::Data(doc) => {
268 if doc.has_included() && !doc.has_data() {
269 errors.push(DocumentValidationError::IncludedWithoutData);
270 }
271
272 if !(doc.has_data() || doc.has_meta()) {
273 errors.push(DocumentValidationError::MissingContent);
274 }
275 match errors.len() {
276 0 => None,
277 _ => Some(errors),
278 }
279 }
280 }
281 }
282}
283
284impl FromStr for JsonApiDocument {
285 type Err = Error;
286
287 fn from_str(s: &str) -> Result<Self> {
304 serde_json::from_str(s).chain_err(|| "Error parsing Document")
305 }
306}
307
308impl Resource {
309 pub fn get_relationship(&self, name: &str) -> Option<&Relationship> {
310 match self.relationships {
311 None => None,
312 Some(ref relationships) => {
313 match relationships.get(name) {
314 None => None,
315 Some(rel) => Some(rel),
316 }
317 }
318 }
319 }
320
321 pub fn get_attribute(&self, name: &str) -> Option<&JsonApiValue> {
355 match self.attributes.get(name) {
356 None => None,
357 Some(val) => Some(val),
358 }
359 }
360
361 pub fn diff(&self, other: Resource) -> std::result::Result<PatchSet, DiffPatchError> {
362 if self._type != other._type {
363 Err(DiffPatchError::IncompatibleTypes(
364 self._type.clone(),
365 other._type.clone(),
366 ))
367 } else {
368
369 let mut self_keys: Vec<String> =
370 self.attributes.iter().map(|(key, _)| key.clone()).collect();
371
372 self_keys.sort();
373
374 let mut other_keys: Vec<String> = other
375 .attributes
376 .iter()
377 .map(|(key, _)| key.clone())
378 .collect();
379
380 other_keys.sort();
381
382 let matching = self_keys
383 .iter()
384 .zip(other_keys.iter())
385 .filter(|&(a, b)| a == b)
386 .count();
387
388 if matching != self_keys.len() {
389 Err(DiffPatchError::DifferentAttributeKeys)
390 } else {
391 let mut patchset = PatchSet::new_for(self);
392
393 for (attr, self_value) in &self.attributes {
394 match other.attributes.get(attr) {
395 None => {
396 error!(
397 "Resource::diff unable to find attribute {:?} in {:?}",
398 attr,
399 other
400 );
401 }
402 Some(other_value) => {
403 if self_value != other_value {
404 patchset.push(Patch {
405 patch_type: PatchType::Attribute,
406 subject: attr.clone(),
407 previous: self_value.clone(),
408 next: other_value.clone(),
409 });
410 }
411 }
412 }
413
414 }
415
416 Ok(patchset)
417 }
418 }
419 }
420
421 pub fn patch(&mut self, patchset: PatchSet) -> Result<Resource> {
422 let mut res = self.clone();
423 for patch in &patchset.patches {
424 res.attributes.insert(
425 patch.subject.clone(),
426 patch.next.clone(),
427 );
428 }
429 Ok(res)
430 }
431}
432
433impl FromStr for Resource {
434 type Err = Error;
435
436 fn from_str(s: &str) -> Result<Self> {
457 serde_json::from_str(s).chain_err(|| "Error parsing resource")
458 }
459}
460
461
462impl Relationship {
463 pub fn as_id(&self) -> std::result::Result<Option<&JsonApiId>, RelationshipAssumptionError> {
464 match self.data {
465 Some(IdentifierData::None) => Ok(None),
466 Some(IdentifierData::Multiple(_)) => Err(RelationshipAssumptionError::RelationshipIsAList),
467 Some(IdentifierData::Single(ref data)) => Ok(Some(&data.id)),
468 None => Ok(None),
469 }
470 }
471
472 pub fn as_ids(&self) -> std::result::Result<Option<JsonApiIds>, RelationshipAssumptionError> {
473 match self.data {
474 Some(IdentifierData::None) => Ok(None),
475 Some(IdentifierData::Single(_)) => Err(RelationshipAssumptionError::RelationshipIsNotAList),
476 Some(IdentifierData::Multiple(ref data)) => Ok(Some(data.iter().map(|x| &x.id).collect())),
477 None => Ok(None),
478 }
479 }
480}
481
482#[derive(Debug, Clone, PartialEq, Copy)]
484pub enum DocumentValidationError {
485 IncludedWithoutData,
486 MissingContent,
487}
488
489#[derive(Debug, Clone, PartialEq, Copy)]
490pub enum JsonApiDataError {
491 AttributeNotFound,
492 IncompatibleAttributeType,
493}
494
495#[derive(Debug, Clone, PartialEq, Copy)]
496pub enum RelationshipAssumptionError {
497 RelationshipIsAList,
498 RelationshipIsNotAList,
499}
500
501#[derive(Debug, Clone, PartialEq)]
502pub enum DiffPatchError {
503 IncompatibleTypes(String, String),
504 DifferentAttributeKeys,
505 NonExistentProperty(String),
506 IncorrectPropertyValue(String),
507}
508
509#[derive(Debug, Clone, PartialEq, Copy)]
510pub enum PatchType {
511 Relationship,
512 Attribute,
513}