payjp_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::de::Visitor;
37    use miniserde::json::Value;
38    use miniserde::Deserialize;
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 {
64                Some(AlwaysTrue)
65            } else {
66                None
67            }
68        }
69    }
70}
71
72/// An alias for data representing a UNIX timestamp.
73pub type Timestamp = i64;
74
75/// Specification of a date interval for use in filtering results by time.
76#[derive(Copy, Clone, Debug, Serialize, Default)]
77pub struct RangeBoundsTs {
78    /// Minimum value to filter by (exclusive)
79    pub gt: Option<Timestamp>,
80    /// Minimum value to filter by (inclusive)
81    pub gte: Option<Timestamp>,
82    /// Maximum value to filter by (exclusive)
83    pub lt: Option<Timestamp>,
84    /// Maximum value to filter by (inclusive)
85    pub lte: Option<Timestamp>,
86}
87
88/// A set of generic request parameters that can be used on
89/// list endpoints to filter their results by some timestamp.
90#[derive(Copy, Clone, Debug, Serialize)]
91#[serde(untagged)]
92pub enum RangeQueryTs {
93    /// Results matching a specific UNIX timestamp
94    Exact(Timestamp),
95    /// Results falling with a date interval
96    Bounds(RangeBoundsTs),
97}
98
99impl RangeQueryTs {
100    /// Filter results to exactly match a given value
101    pub fn eq(value: Timestamp) -> Self {
102        Self::Exact(value)
103    }
104
105    /// Filter results to be after a given value
106    pub fn gt(value: Timestamp) -> Self {
107        Self::Bounds(RangeBoundsTs { gt: Some(value), ..Default::default() })
108    }
109
110    /// Filter results to be after or equal to a given value
111    pub fn gte(value: Timestamp) -> Self {
112        Self::Bounds(RangeBoundsTs { gte: Some(value), ..Default::default() })
113    }
114
115    /// Filter results to be before to a given value
116    pub fn lt(value: Timestamp) -> Self {
117        Self::Bounds(RangeBoundsTs { lt: Some(value), ..Default::default() })
118    }
119
120    /// Filter results to be before or equal to a given value
121    pub fn lte(value: Timestamp) -> Self {
122        Self::Bounds(RangeBoundsTs { lte: Some(value), ..Default::default() })
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    fn urldecode(input: String) -> String {
131        input.replace("%5B", "[").replace("%5D", "]")
132    }
133
134    // A smaller version of `ListCustomer`, used to test de/serialization of `RangeQueryTs`
135    #[derive(Clone, Debug, Default, serde::Serialize)]
136    struct TestListCustomer {
137        #[serde(skip_serializing_if = "Option::is_none")]
138        pub created: Option<RangeQueryTs>,
139        #[serde(skip_serializing_if = "Option::is_none")]
140        pub ending_before: Option<String>,
141        #[serde(skip_serializing_if = "Option::is_none")]
142        pub limit: Option<i64>,
143        #[serde(skip_serializing_if = "Option::is_none")]
144        pub starting_after: Option<String>,
145    }
146
147    impl TestListCustomer {
148        fn new() -> Self {
149            Self::default()
150        }
151    }
152
153    #[test]
154    fn serialize_range_query() {
155        let query = RangeQueryTs::Bounds(RangeBoundsTs {
156            gt: None,
157            gte: Some(1501598702),
158            lt: Some(1504233902),
159            lte: None,
160        });
161        assert_eq!(urldecode(serde_qs::to_string(&query).unwrap()), "gte=1501598702&lt=1504233902");
162
163        let mut params = TestListCustomer::new();
164        params.created = Some(RangeQueryTs::eq(1501598702));
165        params.limit = Some(3);
166        assert_eq!(urldecode(serde_qs::to_string(&params).unwrap()), "created=1501598702&limit=3");
167
168        let mut params = TestListCustomer::new();
169        params.created = Some(RangeQueryTs::gte(1501598702));
170        params.limit = Some(3);
171        assert_eq!(
172            urldecode(serde_qs::to_string(&params).unwrap()),
173            "created[gte]=1501598702&limit=3"
174        );
175
176        let mut params = TestListCustomer::new();
177        params.created = Some(query);
178        params.limit = Some(3);
179        let encoded = urldecode(serde_qs::to_string(&params).unwrap());
180        assert_eq!(encoded, "created[gte]=1501598702&created[lt]=1504233902&limit=3");
181    }
182}