stripe_types/
params.rs

1use serde::{Serialize, Serializer};
2
3/// An implementation detail used for allowing `serde(untagged)` to distinguish
4/// between data that may be "deleted" or not. "deleted" data will always have the
5/// field `deleted: true`.
6#[doc(hidden)]
7#[derive(Copy, Clone, Eq, PartialEq, Debug)]
8pub struct AlwaysTrue;
9
10impl serde::Serialize for AlwaysTrue {
11    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
12    where
13        S: Serializer,
14    {
15        serializer.serialize_bool(true)
16    }
17}
18
19#[cfg(feature = "deserialize")]
20impl<'de> serde::Deserialize<'de> for AlwaysTrue {
21    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
22    where
23        D: serde::Deserializer<'de>,
24    {
25        let bool_: bool = serde::Deserialize::deserialize(deserializer)?;
26        if !bool_ {
27            Err(serde::de::Error::custom("Expected value to always be `true`"))
28        } else {
29            Ok(AlwaysTrue)
30        }
31    }
32}
33
34#[doc(hidden)]
35mod miniserde_deser {
36    use miniserde::Deserialize;
37    use miniserde::de::Visitor;
38    use miniserde::json::Value;
39
40    use crate::miniserde_helpers::FromValueOpt;
41    use crate::{AlwaysTrue, Place};
42
43    impl Deserialize for AlwaysTrue {
44        fn begin(out: &mut Option<Self>) -> &mut dyn Visitor {
45            Place::new(out)
46        }
47    }
48
49    impl Visitor for Place<AlwaysTrue> {
50        fn boolean(&mut self, b: bool) -> miniserde::Result<()> {
51            if b {
52                self.out = Some(AlwaysTrue);
53                Ok(())
54            } else {
55                Err(miniserde::Error)
56            }
57        }
58    }
59
60    impl FromValueOpt for AlwaysTrue {
61        fn from_value(v: Value) -> Option<Self> {
62            let b = bool::from_value(v)?;
63            if b { Some(AlwaysTrue) } else { None }
64        }
65    }
66}
67
68/// An alias for Stripe data representing a UNIX timestamp.
69pub type Timestamp = i64;
70
71/// Specification of a date interval for use in filtering results by time.
72#[derive(Copy, Clone, Debug, Serialize, Default)]
73pub struct RangeBoundsTs {
74    /// Minimum value to filter by (exclusive)
75    pub gt: Option<Timestamp>,
76    /// Minimum value to filter by (inclusive)
77    pub gte: Option<Timestamp>,
78    /// Maximum value to filter by (exclusive)
79    pub lt: Option<Timestamp>,
80    /// Maximum value to filter by (inclusive)
81    pub lte: Option<Timestamp>,
82}
83
84/// A set of generic request parameters that can be used on
85/// list endpoints to filter their results by some timestamp.
86#[derive(Copy, Clone, Debug, Serialize)]
87#[serde(untagged)]
88pub enum RangeQueryTs {
89    /// Results matching a specific UNIX timestamp
90    Exact(Timestamp),
91    /// Results falling with a date interval
92    Bounds(RangeBoundsTs),
93}
94
95impl RangeQueryTs {
96    /// Filter results to exactly match a given value
97    pub fn eq(value: Timestamp) -> Self {
98        Self::Exact(value)
99    }
100
101    /// Filter results to be after a given value
102    pub fn gt(value: Timestamp) -> Self {
103        Self::Bounds(RangeBoundsTs { gt: Some(value), ..Default::default() })
104    }
105
106    /// Filter results to be after or equal to a given value
107    pub fn gte(value: Timestamp) -> Self {
108        Self::Bounds(RangeBoundsTs { gte: Some(value), ..Default::default() })
109    }
110
111    /// Filter results to be before to a given value
112    pub fn lt(value: Timestamp) -> Self {
113        Self::Bounds(RangeBoundsTs { lt: Some(value), ..Default::default() })
114    }
115
116    /// Filter results to be before or equal to a given value
117    pub fn lte(value: Timestamp) -> Self {
118        Self::Bounds(RangeBoundsTs { lte: Some(value), ..Default::default() })
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    fn urldecode(input: String) -> String {
127        input.replace("%5B", "[").replace("%5D", "]")
128    }
129
130    // A smaller version of `ListCustomer`, used to test de/serialization of `RangeQueryTs`
131    #[derive(Clone, Debug, Default, serde::Serialize)]
132    struct TestListCustomer {
133        #[serde(skip_serializing_if = "Option::is_none")]
134        pub created: Option<RangeQueryTs>,
135        #[serde(skip_serializing_if = "Option::is_none")]
136        pub ending_before: Option<String>,
137        #[serde(skip_serializing_if = "Option::is_none")]
138        pub limit: Option<i64>,
139        #[serde(skip_serializing_if = "Option::is_none")]
140        pub starting_after: Option<String>,
141    }
142
143    impl TestListCustomer {
144        fn new() -> Self {
145            Self::default()
146        }
147    }
148
149    #[test]
150    fn serialize_range_query() {
151        let query = RangeQueryTs::Bounds(RangeBoundsTs {
152            gt: None,
153            gte: Some(1501598702),
154            lt: Some(1504233902),
155            lte: None,
156        });
157        assert_eq!(urldecode(serde_qs::to_string(&query).unwrap()), "gte=1501598702&lt=1504233902");
158
159        let mut params = TestListCustomer::new();
160        params.created = Some(RangeQueryTs::eq(1501598702));
161        params.limit = Some(3);
162        assert_eq!(urldecode(serde_qs::to_string(&params).unwrap()), "created=1501598702&limit=3");
163
164        let mut params = TestListCustomer::new();
165        params.created = Some(RangeQueryTs::gte(1501598702));
166        params.limit = Some(3);
167        assert_eq!(
168            urldecode(serde_qs::to_string(&params).unwrap()),
169            "created[gte]=1501598702&limit=3"
170        );
171
172        let mut params = TestListCustomer::new();
173        params.created = Some(query);
174        params.limit = Some(3);
175        let encoded = urldecode(serde_qs::to_string(&params).unwrap());
176        assert_eq!(encoded, "created[gte]=1501598702&created[lt]=1504233902&limit=3");
177    }
178}