diff --git a/src/entities/itemsiter.rs b/src/entities/itemsiter.rs new file mode 100644 index 0000000..f01ddb1 --- /dev/null +++ b/src/entities/itemsiter.rs @@ -0,0 +1,91 @@ +use page::Page; +use serde::Deserialize; + +/// Abstracts away the `next_page` logic into a single stream of items +/// +/// ```no_run +/// # extern crate mammut; +/// # use mammut::{Data, Mastodon}; +/// # use std::error::Error; +/// # fn main() -> Result<(), Box> { +/// # let data = Data { +/// # base: "".into(), +/// # client_id: "".into(), +/// # client_secret: "".into(), +/// # redirect: "".into(), +/// # token: "".into(), +/// # }; +/// let client = Mastodon::from_data(data); +/// let statuses = client.statuses("user-id", None)?; +/// for status in statuses.items_iter() { +/// // do something with `status` +/// } +/// # Ok(()) +/// # } +/// ``` +pub struct ItemsIter<'a, T: Clone + for<'de> Deserialize<'de>> { + page: Page<'a, T>, + buffer: Vec, + cur_idx: usize, + use_initial: bool, +} + +impl<'a, T: Clone + for<'de> Deserialize<'de>> ItemsIter<'a, T> { + pub(crate) fn new(page: Page<'a, T>) -> ItemsIter<'a, T> { + ItemsIter { + page: page, + buffer: vec![], + cur_idx: 0, + use_initial: true, + } + } + + fn need_next_page(&self) -> bool { + self.buffer.is_empty() || + self.cur_idx == self.buffer.len() + } + + fn fill_next_page(&mut self) -> Option<()> { + let items = if let Ok(items) = self.page.next_page() { + items + } else { + return None; + }; + if let Some(items) = items { + self.buffer = items; + self.cur_idx = 0; + Some(()) + } else { + None + } + } +} + +impl<'a, T: Clone+ for<'de> Deserialize<'de>> Iterator for ItemsIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + if self.use_initial { + if self.page.initial_items.is_empty() { + return None; + } + let idx = self.cur_idx; + if self.cur_idx == self.page.initial_items.len() - 1 { + self.cur_idx = 0; + self.use_initial = false; + } else { + self.cur_idx += 1; + } + Some(self.page.initial_items[idx].clone()) + } else { + if self.need_next_page() { + if self.fill_next_page().is_none() { + return None; + } + } + let idx = self.cur_idx; + self.cur_idx += 1; + Some(self.buffer[idx].clone()) + } + } +} diff --git a/src/entities/mod.rs b/src/entities/mod.rs index c82ae5a..9da56b3 100644 --- a/src/entities/mod.rs +++ b/src/entities/mod.rs @@ -6,6 +6,7 @@ pub mod instance; pub mod list; pub mod mention; pub mod notification; +pub(crate) mod itemsiter; pub mod relationship; pub mod report; pub mod search_result; diff --git a/src/lib.rs b/src/lib.rs index 466e93f..5ac1986 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -587,16 +587,42 @@ impl Mastodon { /// /// # Example /// - /// ```ignore + /// ```no_run + /// # extern crate mammut; + /// # use mammut::{Data, Mastodon}; + /// # use std::error::Error; + /// # fn main() -> Result<(), Box> { + /// # let data = Data { + /// # base: "".into(), + /// # client_id: "".into(), + /// # client_secret: "".into(), + /// # redirect: "".into(), + /// # token: "".into(), + /// # }; /// let client = Mastodon::from_data(data); /// let statuses = client.statuses("user-id", None)?; + /// # Ok(()) + /// # } /// ``` /// - /// ```ignore + /// ```no_run + /// # extern crate mammut; + /// # use mammut::{Data, Mastodon, StatusesRequest}; + /// # use std::error::Error; + /// # fn main() -> Result<(), Box> { + /// # let data = Data { + /// # base: "".into(), + /// # client_id: "".into(), + /// # client_secret: "".into(), + /// # redirect: "".into(), + /// # token: "".into(), + /// # }; /// let client = Mastodon::from_data(data); /// let request = StatusesRequest::default() /// .only_media(); /// let statuses = client.statuses("user-id", request)?; + /// # Ok(()) + /// # } /// ``` pub fn statuses<'a, S>(&self, id: &str, request: S) -> Result> where S: Into>> diff --git a/src/page.rs b/src/page.rs index 1a9cf98..6648be9 100644 --- a/src/page.rs +++ b/src/page.rs @@ -3,6 +3,7 @@ use reqwest::Response; use reqwest::header::{Link, RelationType}; use serde::Deserialize; use url::Url; +use entities::itemsiter::ItemsIter; pub struct Page<'a, T: for<'de> Deserialize<'de>> { mastodon: &'a Mastodon, @@ -53,6 +54,13 @@ impl<'a, T: for<'de> Deserialize<'de>> Page<'a, T> { } } +impl<'a, T: Clone + for<'de> Deserialize<'de>> Page<'a, T> { + pub fn items_iter(self) -> impl Iterator + 'a + where T: 'a + { + ItemsIter::new(self) + } +} fn get_links(response: &Response) -> Result<(Option, Option)> { let mut prev = None;