stripe_client_core/
pagination.rs1use std::collections::VecDeque;
2
3use miniserde::Deserialize;
4use serde::Serialize;
5use serde_json::Value;
6use stripe_types::{AsCursorOpt, List, Object, SearchList};
7
8use crate::{RequestBuilder, StripeBlockingClient, StripeClient, StripeMethod};
9
10#[doc(hidden)]
19pub trait PaginableList: Deserialize {
20 type Data;
22
23 fn into_parts(self) -> ListParts<Self::Data>;
25
26 fn from_parts(parts: ListParts<Self::Data>) -> Self;
28
29 fn update_params(&mut self, params: &mut Value);
37}
38
39#[doc(hidden)]
41#[derive(Debug)]
42pub struct ListParts<T> {
43 pub total_count: Option<u64>,
44 pub url: String,
45 pub data: VecDeque<T>,
46 pub has_more: bool,
47}
48
49impl<T> PaginableList for List<T>
50where
51 T: Object,
52 List<T>: Deserialize,
53{
54 type Data = T;
55
56 fn into_parts(self) -> ListParts<Self::Data> {
57 ListParts {
58 total_count: None,
61 url: self.url,
62 data: VecDeque::from(self.data),
63 has_more: self.has_more,
64 }
65 }
66
67 fn from_parts(parts: ListParts<Self::Data>) -> Self {
68 Self { data: Vec::from(parts.data), has_more: parts.has_more, url: parts.url }
69 }
70
71 fn update_params(&mut self, params: &mut Value) {
72 if let Some(new_cursor) = self.data.last().and_then(|l| l.id().as_cursor_opt()) {
73 params["starting_after"] = Value::String(new_cursor.into());
74 } else {
75 self.has_more = false;
76 }
77 }
78}
79
80impl<T> PaginableList for SearchList<T>
81where
82 SearchList<T>: Deserialize,
83{
84 type Data = T;
85
86 fn into_parts(self) -> ListParts<Self::Data> {
89 ListParts {
90 total_count: self.total_count,
91 url: self.url,
92 data: VecDeque::from(self.data),
93 has_more: self.has_more,
94 }
95 }
96
97 fn from_parts(parts: ListParts<Self::Data>) -> Self {
98 Self {
99 url: parts.url,
100 has_more: parts.has_more,
101 data: Vec::from(parts.data),
102 next_page: None,
103 total_count: parts.total_count,
104 }
105 }
106
107 fn update_params(&mut self, params: &mut Value) {
108 if let Some(next_page) = self.next_page.take() {
109 params["page"] = Value::String(next_page);
110 } else {
111 self.has_more = false;
112 }
113 }
114}
115
116pub trait PaginationExt {
119 type Data;
121
122 fn into_paginator(self) -> ListPaginator<Self::Data>;
125}
126
127impl<T> PaginationExt for List<T>
128where
129 T: Sync + Send + 'static,
130 List<T>: PaginableList,
131{
132 type Data = List<T>;
133
134 fn into_paginator(mut self) -> ListPaginator<List<T>> {
135 let mut params = Default::default();
136 self.update_params(&mut params);
137 ListPaginator { page: self, params }
138 }
139}
140
141impl<T> PaginationExt for SearchList<T>
142where
143 T: Sync + Send + 'static,
144 SearchList<T>: PaginableList,
145{
146 type Data = SearchList<T>;
147
148 fn into_paginator(mut self) -> ListPaginator<SearchList<T>> {
149 let mut params = Default::default();
150 self.update_params(&mut params);
151 ListPaginator { page: self, params }
152 }
153}
154
155#[derive(Debug)]
157pub struct ListPaginator<T> {
158 page: T,
159 params: Value,
160}
161
162impl<T> ListPaginator<SearchList<T>> {
163 #[doc(hidden)]
166 pub fn new_search_list(url: impl Into<String>, params: impl Serialize) -> Self {
167 let page = SearchList {
168 url: url.into(),
169 has_more: true,
170 data: vec![],
171 next_page: None,
172 total_count: None,
173 };
174 Self {
175 page,
176 params: serde_json::to_value(params)
177 .expect("all Stripe types implement `Serialize` infallibly"),
179 }
180 }
181}
182
183impl<T> ListPaginator<List<T>> {
184 #[doc(hidden)]
187 pub fn new_list(url: impl Into<String>, params: impl Serialize) -> Self {
188 let page = List { data: vec![], has_more: true, url: url.into() };
189 Self {
190 page,
191 params: serde_json::to_value(params)
192 .expect("all Stripe types implement `Serialize` infallibly"),
193 }
194 }
195}
196
197fn req_builder(url: &str) -> RequestBuilder {
198 RequestBuilder::new(StripeMethod::Get, url.trim_start_matches("/v1"))
199}
200
201impl<T> ListPaginator<T>
202where
203 T: Sync + Send + 'static + PaginableList,
204{
205 pub fn get_all<C: StripeBlockingClient>(self, client: &C) -> Result<Vec<T::Data>, C::Err> {
211 let mut data = VecDeque::new();
212 let mut parts = self.page.into_parts();
213 let mut params = self.params;
214 loop {
215 data.append(&mut parts.data);
217
218 if !parts.has_more {
219 break;
220 }
221
222 let req = req_builder(&parts.url).query(¶ms);
223 let mut next_page: T = req.customize().send_blocking(client)?;
224 next_page.update_params(&mut params);
225 parts = next_page.into_parts();
226 }
227 Ok(Vec::from(data))
228 }
229
230 pub fn stream<C: StripeClient + Clone>(
235 self,
236 client: &C,
237 ) -> impl futures_util::Stream<Item = Result<T::Data, C::Err>> + Unpin {
238 Box::pin(futures_util::stream::unfold(Some((self, client.clone())), Self::unfold_stream))
240 }
241
242 async fn unfold_stream<C: StripeClient + Clone>(
244 state: Option<(Self, C)>,
245 ) -> Option<(Result<T::Data, C::Err>, Option<(Self, C)>)> {
246 let (paginator, client) = state?; let mut parts = paginator.page.into_parts();
248 if let Some(next_val) = parts.data.pop_front() {
249 return Some((
251 Ok(next_val),
252 Some((Self { page: T::from_parts(parts), params: paginator.params }, client)),
253 ));
254 }
255
256 if !parts.has_more {
258 return None;
259 }
260
261 let req = req_builder(&parts.url).query(&paginator.params);
262 match req.customize::<T>().send(&client).await {
263 Ok(mut next_page) => {
264 let mut params = paginator.params;
265 next_page.update_params(&mut params);
266
267 let mut parts = next_page.into_parts();
268
269 let next_val = parts.data.pop_front()?;
270
271 Some((Ok(next_val), Some((Self { page: T::from_parts(parts), params }, client))))
273 }
274 Err(err) => Some((Err(err), None)), }
276 }
277}