use http;
use serde::de::{Deserialize, DeserializeOwned, Deserializer};
use std;
#[derive(Debug)]
pub struct Records<T>
where
T: DeserializeOwned,
{
records: Vec<T>,
next: Option<http::Uri>,
prev: Option<http::Uri>,
}
impl<T> Records<T>
where
T: DeserializeOwned,
{
pub fn records(&self) -> &Vec<T> {
&self.records
}
pub fn next(&self) -> Option<&http::Uri> {
self.next.as_ref()
}
pub fn prev(&self) -> Option<&http::Uri> {
self.prev.as_ref()
}
}
impl<'de, T> Deserialize<'de> for Records<T>
where
T: DeserializeOwned,
{
fn deserialize<D>(d: D) -> std::result::Result<Records<T>, D::Error>
where
D: Deserializer<'de>,
{
let embedded: Embedded<RecordsIntermediate<T>> = Embedded::deserialize(d)?;
if let Some(links) = embedded.links {
Ok(Records {
records: embedded.embedded.records,
next: links.next.and_then(|v| v.uri()),
prev: links.prev.and_then(|v| v.uri()),
})
} else {
Ok(Records {
records: embedded.embedded.records,
next: None,
prev: None,
})
}
}
}
#[derive(Deserialize)]
struct Embedded<T> {
#[serde(rename = "_embedded")]
embedded: T,
#[serde(rename = "_links")]
links: Option<Links>,
}
#[derive(Deserialize)]
struct RecordsIntermediate<T> {
records: Vec<T>,
}
#[derive(Deserialize)]
struct Links {
next: Option<Href>,
prev: Option<Href>,
}
#[derive(Deserialize)]
struct Href {
href: String,
}
impl Href {
fn uri(&self) -> Option<http::Uri> {
self.href.parse().ok()
}
}
#[cfg(test)]
mod records_test {
use super::*;
use serde_json;
#[derive(Deserialize)]
struct Foo {
foo: String,
}
#[test]
fn it_parses_out_a_embedded_records_string() {
let json = r#"
{
"_links": {
"next": {
"href": "/assets?order=asc&limit=10&cursor=NEXT_CURSOR"
},
"prev": {
"href": "/assets?order=asc&limit=10&cursor=PREV_CURSOR"
}
},
"_embedded": {
"records": [
{ "foo": "bar" }
]
}
}"#;
let next: http::Uri = "/assets?order=asc&limit=10&cursor=NEXT_CURSOR"
.parse()
.unwrap();
let prev: http::Uri = "/assets?order=asc&limit=10&cursor=PREV_CURSOR"
.parse()
.unwrap();
let records: Records<Foo> = serde_json::from_str(&json).unwrap();
assert_eq!(records.records().first().unwrap().foo, "bar");
assert_eq!(records.next(), Some(&next));
assert_eq!(records.prev(), Some(&prev));
}
#[test]
fn it_parses_out_none_if_blank() {
let json = r#"
{
"_links": {
"next": {
"href": ""
},
"prev": {
"href": ""
}
},
"_embedded": {
"records": [
{ "foo": "bar" }
]
}
}"#;
let records: Records<Foo> = serde_json::from_str(&json).unwrap();
assert_eq!(records.records().first().unwrap().foo, "bar");
assert_eq!(records.next(), None);
assert_eq!(records.prev(), None);
}
#[test]
fn it_parses_out_if_no_links() {
let json = r#"
{
"_embedded": {
"records": [
{ "foo": "bar" }
]
}
}"#;
let records: Records<Foo> = serde_json::from_str(&json).unwrap();
assert_eq!(records.records().first().unwrap().foo, "bar");
assert_eq!(records.next(), None);
assert_eq!(records.prev(), None);
}
}