use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
pub trait JsonApiResource {
const RESOURCE_TYPE: &'static str;
fn id(&self) -> String;
fn attributes(&self) -> Value;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JsonApiDocument<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<JsonApiData<T>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub errors: Option<Vec<JsonApiError>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub jsonapi: Option<JsonApiVersion>,
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub included: Option<Vec<Resource<Value>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum JsonApiData<T> {
Single(Resource<T>),
Multiple(Vec<Resource<T>>),
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Resource<T> {
#[serde(rename = "type")]
pub resource_type: String,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub relationships: Option<HashMap<String, Relationship>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<Value>,
}
pub struct JsonApiResponse<T: Serialize> {
data: JsonApiData<T>,
links: Option<HashMap<String, String>>,
meta: Option<Value>,
included: Option<Vec<Resource<Value>>>,
}
impl<T: Serialize> JsonApiResponse<T> {
pub fn new(resource: Resource<T>) -> Self {
Self {
data: JsonApiData::Single(resource),
links: None,
meta: None,
included: None,
}
}
pub fn new_multiple(resources: Vec<Resource<T>>) -> Self {
Self {
data: JsonApiData::Multiple(resources),
links: None,
meta: None,
included: None,
}
}
pub fn with_links(mut self, links: HashMap<String, String>) -> Self {
self.links = Some(links);
self
}
pub fn with_meta(mut self, meta: Value) -> Self {
self.meta = Some(meta);
self
}
pub fn with_included(mut self, included: Vec<Resource<Value>>) -> Self {
self.included = Some(included);
self
}
pub fn build(self) -> JsonApiDocument<T> {
JsonApiDocument {
data: Some(self.data),
errors: None,
meta: self.meta,
jsonapi: Some(JsonApiVersion {
version: "1.1".to_string(),
meta: None,
}),
links: self.links,
included: self.included,
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Relationship {
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<RelationshipData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum RelationshipData {
Single(ResourceIdentifier),
Multiple(Vec<ResourceIdentifier>),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ResourceIdentifier {
#[serde(rename = "type")]
pub resource_type: String,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct JsonApiError {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<ErrorSource>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ErrorSource {
#[serde(skip_serializing_if = "Option::is_none")]
pub pointer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameter: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct JsonApiVersion {
pub version: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<Value>,
}
impl JsonApiError {
pub fn builder() -> JsonApiErrorBuilder {
JsonApiErrorBuilder::default()
}
}
#[derive(Default)]
pub struct JsonApiErrorBuilder {
id: Option<String>,
links: Option<HashMap<String, String>>,
status: Option<String>,
code: Option<String>,
title: Option<String>,
detail: Option<String>,
source: Option<ErrorSource>,
meta: Option<Value>,
}
impl JsonApiErrorBuilder {
pub fn status(mut self, status: &str) -> Self {
self.status = Some(status.to_string());
self
}
pub fn code(mut self, code: &str) -> Self {
self.code = Some(code.to_string());
self
}
pub fn title(mut self, title: &str) -> Self {
self.title = Some(title.to_string());
self
}
pub fn detail(mut self, detail: &str) -> Self {
self.detail = Some(detail.to_string());
self
}
pub fn source(mut self, pointer: Option<String>, parameter: Option<String>) -> Self {
self.source = Some(ErrorSource { pointer, parameter });
self
}
pub fn build(self) -> JsonApiError {
JsonApiError {
id: self.id,
links: self.links,
status: self.status,
code: self.code,
title: self.title,
detail: self.detail,
source: self.source,
meta: self.meta,
}
}
}