diff --git a/src/params.rs b/src/params.rs index 4f8410922..bfd1ea2d1 100644 --- a/src/params.rs +++ b/src/params.rs @@ -179,16 +179,19 @@ pub trait Paginable { fn set_last(&mut self, item: Self::O); } -#[derive(Debug)] -pub struct ListPaginator { - pub page: List, - pub params: P, -} - -#[derive(Debug)] -pub struct SearchListPaginator { - pub page: SearchList, - pub params: P, +pub trait PaginableList { + type O: Paginate + DeserializeOwned + Send + Sync + 'static + Clone + std::fmt::Debug; + fn new( + &self, + data: Vec, + url: String, + has_more: bool, + total_count: Option, + ) -> Self; + fn get_data(&self) -> Vec; + fn get_url(&self) -> String; + fn get_total_count(&self) -> Option; + fn has_more(&self) -> bool; } /// A single page of a cursor-paginated list of a search object. @@ -230,151 +233,67 @@ impl Clone for SearchList { } } -impl SearchList { - pub fn paginate

(self, params: P) -> SearchListPaginator { - SearchListPaginator { page: self, params } - } -} - -impl< - T: Paginate + DeserializeOwned + Send + Sync + 'static + Clone + std::fmt::Debug, - P: Clone + Serialize + Send + 'static + std::fmt::Debug, - > SearchListPaginator -where - P: Paginable, +impl PaginableList + for SearchList { - /// Repeatedly queries Stripe for more data until all elements in list are fetched, using - /// Stripe's default page size. - /// - /// Requires `feature = "blocking"`. - #[cfg(feature = "blocking")] - pub fn get_all(self, client: &Client) -> Response> { - let mut data = Vec::with_capacity(self.page.total_count.unwrap_or(0) as usize); - let mut paginator = self; - loop { - if !paginator.page.has_more { - data.extend(paginator.page.data.into_iter()); - break; - } - let next_paginator = paginator.next(client)?; - data.extend(paginator.page.data.into_iter()); - paginator = next_paginator - } - Ok(data) - } - - /// Get all values in this SearchList, consuming self and lazily paginating until all values are fetched. - /// - /// This function repeatedly queries Stripe for more data until all elements in list are fetched, using - /// the page size specified in params, or Stripe's default page size if none is specified. - /// - /// ```no_run - /// # use stripe::{Customer, SearchListCustomers, StripeError, Client}; - /// # use futures_util::TryStreamExt; - /// # async fn run() -> Result<(), StripeError> { - /// # let client = Client::new("sk_test_123"); - /// # let params = SearchListCustomers { ..Default::default() }; - /// - /// let list = Customer::list(&client, ¶ms).await.unwrap().paginate(params); - /// let mut stream = list.stream(&client); - /// - /// // take a value out from the stream - /// if let Some(val) = stream.try_next().await? { - /// println!("GOT = {:?}", val); - /// } - /// - /// // alternatively, you can use stream combinators - /// let all_values = stream.try_collect::>().await?; - /// - /// # Ok(()) - /// # } - /// ``` - /// - /// Requires `feature = ["async", "stream"]`. - #[cfg(all(feature = "async", feature = "stream"))] - pub fn stream( - mut self, - client: &Client, - ) -> impl futures_util::Stream> + Unpin { - // We are going to be popping items off the end of the list, so we need to reverse it. - self.page.data.reverse(); + type O = T; - Box::pin(futures_util::stream::unfold(Some((self, client.clone())), Self::unfold_stream)) + fn new( + &self, + data: Vec, + url: String, + has_more: bool, + total_count: Option, + ) -> SearchList { + Self { object: "".to_string(), url, has_more, data: data, next_page: None, total_count } } - /// unfold a single item from the stream - #[cfg(all(feature = "async", feature = "stream"))] - async fn unfold_stream( - state: Option<(Self, Client)>, - ) -> Option<(Result, Option<(Self, Client)>)> { - let (mut paginator, client) = state?; // If none, we sent the last item in the last iteration - - if paginator.page.data.len() > 1 { - return Some((Ok(paginator.page.data.pop()?), Some((paginator, client)))); - // We have more data on this page - } - - if !paginator.page.has_more { - return Some((Ok(paginator.page.data.pop()?), None)); // Final value of the stream, no errors - } - - match paginator.next(&client).await { - Ok(mut next_paginator) => { - let data = paginator.page.data.pop()?; - next_paginator.page.data.reverse(); - - // Yield last value of thimuts page, the next page (and client) becomes the state - Some((Ok(data), Some((next_paginator, client)))) - } - Err(e) => Some((Err(e), None)), // We ran into an error. The last value of the stream will be the error. - } + fn get_data(&self) -> Vec { + self.data.clone() } + fn get_url(&self) -> String { + self.url.clone() + } + fn get_total_count(&self) -> Option { + self.total_count.clone() + } + fn has_more(&self) -> bool { + self.has_more.clone() + } +} - /// Fetch an additional page of data from stripe. - pub fn next(&self, client: &Client) -> Response { - if let Some(last) = self.page.data.last() { - if self.page.url.starts_with("/v1/") { - let path = self.page.url.trim_start_matches("/v1/").to_string(); // the url we get back is prefixed - - // clone the params and set the cursor - let params_next = { - let mut p = self.params.clone(); - p.set_last(last.clone()); - p - }; - - let page = client.get_query(&path, ¶ms_next); +impl PaginableList + for List +{ + type O = T; - SearchListPaginator::create_paginator(page, params_next) - } else { - err(StripeError::UnsupportedVersion) - } - } else { - ok(SearchListPaginator { - page: SearchList { - object: self.page.object.clone(), - data: Vec::new(), - has_more: false, - total_count: self.page.total_count, - url: self.page.url.clone(), - next_page: self.page.next_page.clone(), - }, - params: self.params.clone(), - }) - } + fn new( + &self, + data: Vec, + url: String, + has_more: bool, + total_count: Option, + ) -> List { + Self { url, has_more, data: data, total_count } } - /// Pin a new future which maps the result inside the page future into - /// a SearchListPaginator - #[cfg(feature = "async")] - fn create_paginator(page: Response>, params: P) -> Response { - use futures_util::FutureExt; - Box::pin(page.map(|page| page.map(|page| SearchListPaginator { page, params }))) + fn get_data(&self) -> Vec { + self.data.clone() + } + fn get_url(&self) -> String { + self.url.clone() + } + fn get_total_count(&self) -> Option { + self.total_count.clone() } + fn has_more(&self) -> bool { + self.has_more.clone() + } +} - #[cfg(feature = "blocking")] - fn create_paginator(page: Response>, params: P) -> Response { - page.map(|page| SearchListPaginator { page, params }) +impl SearchList { + pub fn paginate

(self, params: P) -> ListPaginator, P> { + ListPaginator { page: self, params } } } @@ -407,33 +326,39 @@ impl Clone for List { } impl List { - pub fn paginate

(self, params: P) -> ListPaginator { + pub fn paginate

(self, params: P) -> ListPaginator, P> { ListPaginator { page: self, params } } } +#[derive(Debug)] +pub struct ListPaginator { + pub page: T, + pub params: P, +} + impl< - T: Paginate + DeserializeOwned + Send + Sync + 'static + Clone + std::fmt::Debug, + T: PaginableList + Send + DeserializeOwned + 'static, P: Clone + Serialize + Send + 'static + std::fmt::Debug, > ListPaginator where - P: Paginable, + P: Paginable, { /// Repeatedly queries Stripe for more data until all elements in list are fetched, using /// Stripe's default page size. /// /// Requires `feature = "blocking"`. #[cfg(feature = "blocking")] - pub fn get_all(self, client: &Client) -> Response> { - let mut data = Vec::with_capacity(self.page.total_count.unwrap_or(0) as usize); + pub fn get_all(self, client: &Client) -> Response> { + let mut data = Vec::with_capacity(self.page.get_total_count().unwrap_or(0) as usize); let mut paginator = self; loop { - if !paginator.page.has_more { - data.extend(paginator.page.data.into_iter()); + if !paginator.page.has_more() { + data.extend(paginator.page.get_data().into_iter()); break; } let next_paginator = paginator.next(client)?; - data.extend(paginator.page.data.into_iter()); + data.extend(paginator.page.get_data().into_iter()); paginator = next_paginator } Ok(data) @@ -471,9 +396,9 @@ where pub fn stream( mut self, client: &Client, - ) -> impl futures_util::Stream> + Unpin { + ) -> impl futures_util::Stream> + Unpin { // We are going to be popping items off the end of the list, so we need to reverse it. - self.page.data.reverse(); + self.page.get_data().reverse(); Box::pin(futures_util::stream::unfold(Some((self, client.clone())), Self::unfold_stream)) } @@ -482,22 +407,22 @@ where #[cfg(all(feature = "async", feature = "stream"))] async fn unfold_stream( state: Option<(Self, Client)>, - ) -> Option<(Result, Option<(Self, Client)>)> { + ) -> Option<(Result, Option<(Self, Client)>)> { let (mut paginator, client) = state?; // If none, we sent the last item in the last iteration - if paginator.page.data.len() > 1 { - return Some((Ok(paginator.page.data.pop()?), Some((paginator, client)))); + if paginator.page.get_data().len() > 1 { + return Some((Ok(paginator.page.get_data().pop()?), Some((paginator, client)))); // We have more data on this page } - if !paginator.page.has_more { - return Some((Ok(paginator.page.data.pop()?), None)); // Final value of the stream, no errors + if !paginator.page.has_more() { + return Some((Ok(paginator.page.get_data().pop()?), None)); // Final value of the stream, no errors } match paginator.next(&client).await { Ok(mut next_paginator) => { - let data = paginator.page.data.pop()?; - next_paginator.page.data.reverse(); + let data = paginator.page.get_data().pop()?; + next_paginator.page.get_data().reverse(); // Yield last value of thimuts page, the next page (and client) becomes the state Some((Ok(data), Some((next_paginator, client)))) @@ -508,9 +433,9 @@ where /// Fetch an additional page of data from stripe. pub fn next(&self, client: &Client) -> Response { - if let Some(last) = self.page.data.last() { - if self.page.url.starts_with("/v1/") { - let path = self.page.url.trim_start_matches("/v1/").to_string(); // the url we get back is prefixed + if let Some(last) = self.page.get_data().last() { + if self.page.get_url().starts_with("/v1/") { + let path = self.page.get_url().trim_start_matches("/v1/").to_string(); // the url we get back is prefixed // clone the params and set the cursor let params_next = { @@ -527,12 +452,12 @@ where } } else { ok(ListPaginator { - page: List { - data: Vec::new(), - has_more: false, - total_count: self.page.total_count, - url: self.page.url.clone(), - }, + page: self.page.new( + Vec::new(), + self.page.get_url(), + self.page.has_more(), + self.page.get_total_count(), + ), params: self.params.clone(), }) } @@ -541,13 +466,13 @@ where /// Pin a new future which maps the result inside the page future into /// a ListPaginator #[cfg(feature = "async")] - fn create_paginator(page: Response>, params: P) -> Response { + fn create_paginator(page: Response, params: P) -> Response { use futures_util::FutureExt; Box::pin(page.map(|page| page.map(|page| ListPaginator { page, params }))) } #[cfg(feature = "blocking")] - fn create_paginator(page: Response>, params: P) -> Response { + fn create_paginator(page: Response, params: P) -> Response { page.map(|page| ListPaginator { page, params }) } }