payjp_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 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#[derive(Debug)]
67#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
68pub struct List<T> {
69    /// A page of data.
70    pub data: Vec<T>,
71    /// If true, making another request with our new url will yield more data.
72    pub has_more: bool,
73    /// The total number of results.
74    pub count: Option<u64>,
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("count", &self.count)?;
93        ser.serialize_field("url", &self.url)?;
94        ser.serialize_field("object", "list")?;
95        ser.end()
96    }
97}
98
99impl<T: Clone> Clone for List<T> {
100    fn clone(&self) -> Self {
101        List {
102            data: self.data.clone(),
103            has_more: self.has_more,
104            count: self.count,
105            url: self.url.clone(),
106        }
107    }
108}
109
110#[doc(hidden)]
111mod impl_deserialize {
112    use miniserde::de::{Map, Visitor};
113    use miniserde::json::Value;
114    use miniserde::{make_place, Deserialize, Error};
115
116    use crate::miniserde_helpers::FromValueOpt;
117    use crate::{List};
118    make_place!(Place);
119
120    impl<T: Deserialize> Deserialize for List<T> {
121        fn begin(out: &mut Option<Self>) -> &mut dyn Visitor {
122            Place::new(out)
123        }
124    }
125
126    impl<T: Deserialize> Visitor for Place<List<T>> {
127        fn map(&mut self) -> miniserde::Result<Box<dyn Map + '_>> {
128            Ok(Box::new(ListBuilder {
129                out: &mut self.out,
130                data: Deserialize::default(),
131                has_more: Deserialize::default(),
132                count: Deserialize::default(),
133                url: Deserialize::default(),
134            }))
135        }
136    }
137
138    struct ListBuilder<'a, T> {
139        out: &'a mut Option<List<T>>,
140        data: Option<Vec<T>>,
141        has_more: Option<bool>,
142        count: Option<Option<u64>>,
143        url: Option<String>,
144    }
145
146    impl<'a, T: Deserialize> Map for ListBuilder<'a, T> {
147        fn key(&mut self, k: &str) -> miniserde::Result<&mut dyn Visitor> {
148            match k {
149                "url" => Ok(Deserialize::begin(&mut self.url)),
150                "data" => Ok(Deserialize::begin(&mut self.data)),
151                "has_more" => Ok(Deserialize::begin(&mut self.has_more)),
152                "count" => Ok(Deserialize::begin(&mut self.count)),
153                _ => Ok(<dyn Visitor>::ignore()),
154            }
155        }
156
157        fn finish(&mut self) -> miniserde::Result<()> {
158            let url = self.url.take().ok_or(Error)?;
159            let data = self.data.take().ok_or(Error)?;
160            let has_more = self.has_more.ok_or(Error)?;
161            let count = self.count.ok_or(Error)?;
162            *self.out = Some(List { data, has_more, count, url });
163            Ok(())
164        }
165    }
166
167    impl<T: FromValueOpt> FromValueOpt for List<T> {
168        fn from_value(v: Value) -> Option<Self> {
169            let mut data: Option<Vec<T>> = None;
170            let mut has_more: Option<bool> = None;
171            let mut count: Option<Option<u64>> = Some(None);
172            let mut url: Option<String> = None;
173            let Value::Object(obj) = v else {
174                return None;
175            };
176            for (k, v) in obj {
177                match k.as_str() {
178                    "has_more" => has_more = Some(bool::from_value(v)?),
179                    "data" => data = Some(FromValueOpt::from_value(v)?),
180                    "url" => url = Some(FromValueOpt::from_value(v)?),
181                    "count" => count = Some(FromValueOpt::from_value(v)?),
182                    _ => {}
183                }
184            }
185            Some(Self { data: data?, has_more: has_more?, count: count?, url: url? })
186        }
187    }
188}