stripe_types/
pagination.rs

1use std::fmt::Debug;
2use std::str::FromStr;
3
4use serde::{Serialize, Serializer};
5
6#[doc(hidden)]
7pub trait FromCursor {
8    fn from_cursor(val: &str) -> Option<Self>
9    where
10        Self: Sized;
11}
12
13impl FromCursor for smol_str::SmolStr {
14    fn from_cursor(val: &str) -> Option<Self> {
15        Self::from_str(val).ok()
16    }
17}
18
19impl<T: FromCursor> FromCursor for Option<T> {
20    fn from_cursor(val: &str) -> Option<Self> {
21        Some(T::from_cursor(val))
22    }
23}
24
25#[doc(hidden)]
26pub trait AsCursorOpt {
27    fn as_cursor_opt(&self) -> Option<&str>;
28}
29
30#[doc(hidden)]
31pub trait AsCursor {
32    fn as_cursor(&self) -> &str;
33}
34
35impl AsCursor for smol_str::SmolStr {
36    fn as_cursor(&self) -> &str {
37        self.as_str()
38    }
39}
40
41impl<T: AsCursor> AsCursorOpt for T {
42    fn as_cursor_opt(&self) -> Option<&str> {
43        Some(self.as_cursor())
44    }
45}
46
47impl<T: AsCursor> AsCursorOpt for Option<T> {
48    fn as_cursor_opt(&self) -> Option<&str> {
49        self.as_ref().map(|id| id.as_cursor())
50    }
51}
52
53/// Implemented by types which represent stripe objects.
54pub trait Object {
55    /// The canonical id type for this object.
56    type Id: AsCursorOpt + FromCursor;
57
58    /// The id of the object.
59    fn id(&self) -> &Self::Id;
60
61    /// The owned id of the object.
62    fn into_id(self) -> Self::Id;
63}
64
65/// A single page of a cursor-paginated list of an object.
66///
67/// For more details, see <https://stripe.com/docs/api/pagination>
68#[derive(Debug)]
69#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
70pub struct List<T> {
71    /// A page of data.
72    pub data: Vec<T>,
73    /// If true, making another request with our new url will yield more data.
74    pub has_more: bool,
75    /// The base endpoint we're targeting.
76    pub url: String,
77}
78
79// Manually implementing this because we need to add the "object": "list" key. The workarounds
80// mentioned in https://github.com/serde-rs/serde/issues/760 require adding a 1-enum variant, which has the downside
81// that either this field is public (so consumers now have to add this meaningless field) or it is private (and
82// we have a breaking change where `List` cannot be constructed with struct literal syntax)
83impl<T: Serialize> Serialize for List<T> {
84    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
85    where
86        S: Serializer,
87    {
88        use serde::ser::SerializeStruct;
89        let mut ser = serializer.serialize_struct("List", 5)?;
90        ser.serialize_field("data", &self.data)?;
91        ser.serialize_field("has_more", &self.has_more)?;
92        ser.serialize_field("url", &self.url)?;
93        ser.serialize_field("object", "list")?;
94        ser.end()
95    }
96}
97
98impl<T: Clone> Clone for List<T> {
99    fn clone(&self) -> Self {
100        List { data: self.data.clone(), has_more: self.has_more, url: self.url.clone() }
101    }
102}
103
104/// A single page of a cursor-paginated list of a search object.
105///
106/// For more details, see <https://stripe.com/docs/api/pagination/search>
107#[derive(Debug, Serialize)]
108#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
109pub struct SearchList<T> {
110    /// The base endpoint we're targeting.
111    pub url: String,
112    /// If true, making another request with our new url will yield more data.
113    pub has_more: bool,
114    /// A page of data.
115    pub data: Vec<T>,
116    /// The next url to query if we want to continue paginating.
117    pub next_page: Option<String>,
118    /// The total number of results.
119    pub total_count: Option<u64>,
120}
121
122impl<T: Clone> Clone for SearchList<T> {
123    fn clone(&self) -> Self {
124        SearchList {
125            data: self.data.clone(),
126            has_more: self.has_more,
127            total_count: self.total_count,
128            url: self.url.clone(),
129            next_page: self.next_page.clone(),
130        }
131    }
132}
133
134#[doc(hidden)]
135mod impl_deserialize {
136    use miniserde::de::{Map, Visitor};
137    use miniserde::json::Value;
138    use miniserde::{Deserialize, Error, make_place};
139
140    use crate::miniserde_helpers::FromValueOpt;
141    use crate::{List, SearchList};
142    make_place!(Place);
143
144    impl<T: Deserialize> Deserialize for List<T> {
145        fn begin(out: &mut Option<Self>) -> &mut dyn Visitor {
146            Place::new(out)
147        }
148    }
149
150    impl<T: Deserialize> Visitor for Place<List<T>> {
151        fn map(&mut self) -> miniserde::Result<Box<dyn Map + '_>> {
152            Ok(Box::new(ListBuilder {
153                out: &mut self.out,
154                data: Deserialize::default(),
155                has_more: Deserialize::default(),
156                url: Deserialize::default(),
157            }))
158        }
159    }
160
161    struct ListBuilder<'a, T> {
162        out: &'a mut Option<List<T>>,
163        data: Option<Vec<T>>,
164        has_more: Option<bool>,
165        url: Option<String>,
166    }
167
168    impl<T: Deserialize> Map for ListBuilder<'_, T> {
169        fn key(&mut self, k: &str) -> miniserde::Result<&mut dyn Visitor> {
170            match k {
171                "url" => Ok(Deserialize::begin(&mut self.url)),
172                "data" => Ok(Deserialize::begin(&mut self.data)),
173                "has_more" => Ok(Deserialize::begin(&mut self.has_more)),
174                _ => Ok(<dyn Visitor>::ignore()),
175            }
176        }
177
178        fn finish(&mut self) -> miniserde::Result<()> {
179            let url = self.url.take().ok_or(Error)?;
180            let data = self.data.take().ok_or(Error)?;
181            let has_more = self.has_more.ok_or(Error)?;
182            *self.out = Some(List { data, has_more, url });
183            Ok(())
184        }
185    }
186
187    impl<T: FromValueOpt> FromValueOpt for List<T> {
188        fn from_value(v: Value) -> Option<Self> {
189            let mut data: Option<Vec<T>> = None;
190            let mut has_more: Option<bool> = None;
191            let mut url: Option<String> = None;
192            let Value::Object(obj) = v else {
193                return None;
194            };
195            for (k, v) in obj {
196                match k.as_str() {
197                    "has_more" => has_more = Some(bool::from_value(v)?),
198                    "data" => data = Some(FromValueOpt::from_value(v)?),
199                    "url" => url = Some(FromValueOpt::from_value(v)?),
200                    _ => {}
201                }
202            }
203            Some(Self { data: data?, has_more: has_more?, url: url? })
204        }
205    }
206
207    impl<T: Deserialize> Deserialize for SearchList<T> {
208        fn begin(out: &mut Option<Self>) -> &mut dyn Visitor {
209            Place::new(out)
210        }
211    }
212
213    struct SearchListBuilder<'a, T> {
214        out: &'a mut Option<SearchList<T>>,
215        data: Option<Vec<T>>,
216        has_more: Option<bool>,
217        total_count: Option<Option<u64>>,
218        url: Option<String>,
219        next_page: Option<Option<String>>,
220    }
221
222    impl<T: Deserialize> Visitor for Place<SearchList<T>> {
223        fn map(&mut self) -> miniserde::Result<Box<dyn Map + '_>> {
224            Ok(Box::new(SearchListBuilder {
225                out: &mut self.out,
226                data: Deserialize::default(),
227                has_more: Deserialize::default(),
228                total_count: Deserialize::default(),
229                url: Deserialize::default(),
230                next_page: Deserialize::default(),
231            }))
232        }
233    }
234
235    impl<T: Deserialize> Map for SearchListBuilder<'_, T> {
236        fn key(&mut self, k: &str) -> miniserde::Result<&mut dyn Visitor> {
237            match k {
238                "url" => Ok(Deserialize::begin(&mut self.url)),
239                "data" => Ok(Deserialize::begin(&mut self.data)),
240                "has_more" => Ok(Deserialize::begin(&mut self.has_more)),
241                "total_count" => Ok(Deserialize::begin(&mut self.total_count)),
242                "next_page" => Ok(Deserialize::begin(&mut self.next_page)),
243                _ => Ok(<dyn Visitor>::ignore()),
244            }
245        }
246
247        fn finish(&mut self) -> miniserde::Result<()> {
248            let url = self.url.take().ok_or(Error)?;
249            let data = self.data.take().ok_or(Error)?;
250            let has_more = self.has_more.take().ok_or(Error)?;
251            let total_count = self.total_count.take().ok_or(Error)?;
252            let next_page = self.next_page.take().ok_or(Error)?;
253            *self.out = Some(SearchList { data, has_more, total_count, url, next_page });
254            Ok(())
255        }
256    }
257}