rustauth_stripe/stripe_api/
paginated_list.rs1use std::future::Future;
2
3use serde_json::{json, Value};
4
5use super::{StripeApiError, StripeClient};
6
7pub(crate) const STRIPE_LIST_PAGE_LIMIT: u64 = 100;
10const MAX_STRIPE_LIST_PAGES: usize = 100;
12
13impl StripeClient {
14 pub async fn list_subscriptions_all(&self, mut params: Value) -> Result<Value, StripeApiError> {
16 self.paginate_list(
17 |page_params| self.list_subscriptions(page_params),
18 &mut params,
19 )
20 .await
21 }
22
23 pub async fn list_subscription_schedules_all(
25 &self,
26 mut params: Value,
27 ) -> Result<Value, StripeApiError> {
28 self.paginate_list(
29 |page_params| self.list_subscription_schedules(page_params),
30 &mut params,
31 )
32 .await
33 }
34
35 pub async fn list_customers_all(&self, mut params: Value) -> Result<Value, StripeApiError> {
37 self.paginate_list(|page_params| self.list_customers(page_params), &mut params)
38 .await
39 }
40
41 pub async fn find_customer_from_search<F>(
43 &self,
44 query: &str,
45 mut predicate: F,
46 ) -> Result<Option<Value>, StripeApiError>
47 where
48 F: FnMut(&Value) -> bool,
49 {
50 let mut page_token: Option<String> = None;
51 let mut pages = 0usize;
52 loop {
53 if pages >= MAX_STRIPE_LIST_PAGES {
54 return Ok(None);
55 }
56 pages += 1;
57 let search_result = self
58 .search_customers_page(query, page_token.as_deref())
59 .await?;
60 if let Some(found) = search_result
61 .get("data")
62 .and_then(Value::as_array)
63 .and_then(|customers| {
64 customers
65 .iter()
66 .find(|customer| predicate(customer))
67 .cloned()
68 })
69 {
70 return Ok(Some(found));
71 }
72 page_token = search_result
73 .get("next_page")
74 .and_then(Value::as_str)
75 .map(str::to_owned);
76 if page_token.is_none() {
77 return Ok(None);
78 }
79 }
80 }
81
82 pub async fn find_customer<F>(
84 &self,
85 mut params: Value,
86 mut predicate: F,
87 ) -> Result<Option<Value>, StripeApiError>
88 where
89 F: FnMut(&Value) -> bool,
90 {
91 let mut pages = 0usize;
92 loop {
93 if pages >= MAX_STRIPE_LIST_PAGES {
94 return Ok(None);
95 }
96 pages += 1;
97 set_list_page_params(&mut params);
98 let page = self.list_customers(params.clone()).await?;
99 if let Some(found) = page
100 .get("data")
101 .and_then(Value::as_array)
102 .and_then(|customers| {
103 customers
104 .iter()
105 .find(|customer| predicate(customer))
106 .cloned()
107 })
108 {
109 return Ok(Some(found));
110 }
111 if !stripe_list_has_more(&page) {
112 return Ok(None);
113 }
114 let Some(last_id) = last_list_item_id(&page) else {
115 return Ok(None);
116 };
117 set_starting_after(&mut params, last_id);
118 }
119 }
120
121 pub async fn find_subscription<F>(
123 &self,
124 mut params: Value,
125 mut predicate: F,
126 ) -> Result<Option<Value>, StripeApiError>
127 where
128 F: FnMut(&Value) -> bool,
129 {
130 let mut pages = 0usize;
131 loop {
132 if pages >= MAX_STRIPE_LIST_PAGES {
133 return Ok(None);
134 }
135 pages += 1;
136 set_list_page_params(&mut params);
137 let page = self.list_subscriptions(params.clone()).await?;
138 if let Some(found) = page
139 .get("data")
140 .and_then(Value::as_array)
141 .and_then(|subscriptions| subscriptions.iter().find(|sub| predicate(sub)).cloned())
142 {
143 return Ok(Some(found));
144 }
145 if !stripe_list_has_more(&page) {
146 return Ok(None);
147 }
148 let Some(last_id) = last_list_item_id(&page) else {
149 return Ok(None);
150 };
151 set_starting_after(&mut params, last_id);
152 }
153 }
154
155 pub async fn find_subscription_schedule<F>(
157 &self,
158 mut params: Value,
159 mut predicate: F,
160 ) -> Result<Option<Value>, StripeApiError>
161 where
162 F: FnMut(&Value) -> bool,
163 {
164 let mut pages = 0usize;
165 loop {
166 if pages >= MAX_STRIPE_LIST_PAGES {
167 return Ok(None);
168 }
169 pages += 1;
170 set_list_page_params(&mut params);
171 let page = self.list_subscription_schedules(params.clone()).await?;
172 if let Some(found) = page
173 .get("data")
174 .and_then(Value::as_array)
175 .and_then(|schedules| {
176 schedules
177 .iter()
178 .find(|schedule| predicate(schedule))
179 .cloned()
180 })
181 {
182 return Ok(Some(found));
183 }
184 if !stripe_list_has_more(&page) {
185 return Ok(None);
186 }
187 let Some(last_id) = last_list_item_id(&page) else {
188 return Ok(None);
189 };
190 set_starting_after(&mut params, last_id);
191 }
192 }
193
194 async fn paginate_list<F, Fut>(
195 &self,
196 fetch_page: F,
197 params: &mut Value,
198 ) -> Result<Value, StripeApiError>
199 where
200 F: Fn(Value) -> Fut,
201 Fut: Future<Output = Result<Value, StripeApiError>>,
202 {
203 let mut merged = Vec::new();
204 let mut pages = 0usize;
205 loop {
206 if pages >= MAX_STRIPE_LIST_PAGES {
207 break;
208 }
209 pages += 1;
210 set_list_page_params(params);
211 let page = fetch_page(params.clone()).await?;
212 if let Some(data) = page.get("data").and_then(Value::as_array) {
213 merged.extend(data.iter().cloned());
214 }
215 if !stripe_list_has_more(&page) {
216 break;
217 }
218 let Some(last_id) = last_list_item_id(&page) else {
219 break;
220 };
221 set_starting_after(params, last_id);
222 }
223 Ok(json!({
224 "object": "list",
225 "data": merged,
226 "has_more": false,
227 }))
228 }
229}
230
231fn set_list_page_params(params: &mut Value) {
232 let Some(object) = params.as_object_mut() else {
233 return;
234 };
235 object.insert("limit".to_owned(), json!(STRIPE_LIST_PAGE_LIMIT));
236}
237
238fn set_starting_after(params: &mut Value, id: &str) {
239 let Some(object) = params.as_object_mut() else {
240 return;
241 };
242 object.insert("starting_after".to_owned(), json!(id));
243}
244
245fn stripe_list_has_more(page: &Value) -> bool {
246 page.get("has_more")
247 .and_then(Value::as_bool)
248 .unwrap_or(false)
249}
250
251fn last_list_item_id(page: &Value) -> Option<&str> {
252 page.get("data")?.as_array()?.last()?.get("id")?.as_str()
253}