summaryrefslogtreecommitdiffstats
path: root/src/async/page.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/async/page.rs')
-rw-r--r--src/async/page.rs95
1 files changed, 95 insertions, 0 deletions
diff --git a/src/async/page.rs b/src/async/page.rs
new file mode 100644
index 0000000..4ef4161
--- /dev/null
+++ b/src/async/page.rs
@@ -0,0 +1,95 @@
+use super::{client, deserialize, Authenticate};
+use crate::{
+ entities::{account::Account, card::Card, context::Context, status::Status},
+ errors::{Error, Result},
+};
+use http_types::{Method, Request, Response};
+use hyper_old_types::header::{parsing, Link, RelationType};
+use smol::{prelude::*, Async};
+use std::{
+ fmt::Debug,
+ net::{TcpStream, ToSocketAddrs},
+};
+use url::Url;
+
+// link header name
+const LINK: &str = "link";
+
+#[derive(Debug)]
+pub struct Page<'client, T, A: Authenticate + Debug + 'client> {
+ next: Option<Request>,
+ prev: Option<Request>,
+ auth: &'client A,
+ _marker: std::marker::PhantomData<T>,
+}
+impl<'client, T: serde::de::DeserializeOwned, A: Authenticate + Debug + 'client>
+ Page<'client, T, A>
+{
+ pub fn new(next: Request, auth: &'client A) -> Page<'client, T, A> {
+ Page {
+ next: Some(next),
+ prev: None,
+ auth,
+ _marker: std::marker::PhantomData,
+ }
+ }
+
+ pub async fn next_page(&mut self) -> Result<Option<Vec<T>>> {
+ let mut req = if let Some(next) = self.next.take() {
+ next
+ } else {
+ return Ok(None);
+ };
+ Ok(self.send(req).await?)
+ }
+
+ pub async fn prev_page(&mut self) -> Result<Option<Vec<T>>> {
+ let req = if let Some(prev) = self.prev.take() {
+ prev
+ } else {
+ return Ok(None);
+ };
+ Ok(self.send(req).await?)
+ }
+
+ async fn send(&mut self, mut req: Request) -> Result<Option<Vec<T>>> {
+ self.auth.authenticate(&mut req).await?;
+ log::trace!("Request: {:?}", req);
+ let response = client::fetch(req).await?;
+ log::trace!("Response: {:?}", response);
+ self.fill_links_from_resp(&response)?;
+ let items = deserialize(response).await?;
+ Ok(items)
+ }
+
+ fn fill_links_from_resp(&mut self, response: &Response) -> Result<()> {
+ let (prev, next) = get_links(&response)?;
+ self.prev = prev.map(|url| Request::new(Method::Get, url));
+ self.next = next.map(|url| Request::new(Method::Get, url));
+ Ok(())
+ }
+}
+
+fn get_links(response: &Response) -> Result<(Option<Url>, Option<Url>)> {
+ let mut prev = None;
+ let mut next = None;
+
+ if let Some(link_header) = response.header(LINK) {
+ let link_header = link_header.as_str();
+ let link_header = link_header.as_bytes();
+ let link_header: Link = parsing::from_raw_str(&link_header)?;
+ for value in link_header.values() {
+ if let Some(relations) = value.rel() {
+ if relations.contains(&RelationType::Next) {
+ next = Some(Url::parse(value.link())?);
+ }
+
+ if relations.contains(&RelationType::Prev) {
+ prev = Some(Url::parse(value.link())?);
+ }
+ }
+ }
+ }
+
+ Ok((prev, next))
+}