pinboard_rs/api/
params.rs1use std::borrow::Cow;
8
9use chrono::{DateTime, NaiveDate, Utc};
10use url::Url;
11
12pub trait ParamValue<'a> {
14 #[allow(clippy::wrong_self_convention)]
15 fn as_value(&self) -> Cow<'a, str>;
17}
18
19impl ParamValue<'static> for bool {
20 fn as_value(&self) -> Cow<'static, str> {
21 if *self {
22 "true".into()
23 } else {
24 "false".into()
25 }
26 }
27}
28
29impl<'a> ParamValue<'a> for &'a str {
30 fn as_value(&self) -> Cow<'a, str> {
31 (*self).into()
32 }
33}
34
35impl ParamValue<'static> for String {
36 fn as_value(&self) -> Cow<'static, str> {
37 self.clone().into()
38 }
39}
40
41impl<'a> ParamValue<'a> for &'a String {
42 fn as_value(&self) -> Cow<'a, str> {
43 (*self).into()
44 }
45}
46
47impl<'a> ParamValue<'a> for Cow<'a, str> {
48 fn as_value(&self) -> Cow<'a, str> {
49 self.clone()
50 }
51}
52
53impl<'a, 'b: 'a> ParamValue<'a> for &'b Cow<'a, str> {
54 fn as_value(&self) -> Cow<'a, str> {
55 (*self).clone()
56 }
57}
58
59impl ParamValue<'static> for u8 {
60 fn as_value(&self) -> Cow<'static, str> {
61 format!("{}", self).into()
62 }
63}
64
65impl ParamValue<'static> for u64 {
66 fn as_value(&self) -> Cow<'static, str> {
67 format!("{}", self).into()
68 }
69}
70
71impl ParamValue<'static> for f64 {
72 fn as_value(&self) -> Cow<'static, str> {
73 format!("{}", self).into()
74 }
75}
76
77impl ParamValue<'static> for DateTime<Utc> {
78 fn as_value(&self) -> Cow<'static, str> {
79 self.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
80 .into()
81 }
82}
83
84impl ParamValue<'static> for NaiveDate {
85 fn as_value(&self) -> Cow<'static, str> {
86 format!("{}", self.format("%Y-%m-%d")).into()
87 }
88}
89
90impl ParamValue<'static> for &url::Url {
91 fn as_value(&self) -> Cow<'static, str> {
92 format!("{}", self).into()
93 }
94}
95
96#[derive(Debug, Default, Clone)]
98pub struct QueryParams<'a> {
99 params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
100}
101
102impl<'a> QueryParams<'a> {
103 pub fn push<'b, K, V>(&mut self, key: K, value: V) -> &mut Self
105 where
106 K: Into<Cow<'a, str>>,
107 V: ParamValue<'b>,
108 'b: 'a,
109 {
110 self.params.push((key.into(), value.as_value()));
111 self
112 }
113
114 pub fn push_opt<'b, K, V>(&mut self, key: K, value: Option<V>) -> &mut Self
116 where
117 K: Into<Cow<'a, str>>,
118 V: ParamValue<'b>,
119 'b: 'a,
120 {
121 if let Some(value) = value {
122 self.params.push((key.into(), value.as_value()));
123 }
124 self
125 }
126
127 pub fn extend<'b, I, K, V>(&mut self, iter: I) -> &mut Self
129 where
130 I: Iterator<Item = (K, V)>,
131 K: Into<Cow<'a, str>>,
132 V: ParamValue<'b>,
133 'b: 'a,
134 {
135 self.params
136 .extend(iter.map(|(key, value)| (key.into(), value.as_value())));
137 self
138 }
139
140 pub fn add_to_url(&self, url: &mut Url) {
142 let mut pairs = url.query_pairs_mut();
143 pairs.extend_pairs(self.params.iter());
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use crate::api::ParamValue;
150
151 #[test]
152 fn bool_str() {
153 let items = &[(true, "true"), (false, "false")];
154
155 for (i, s) in items {
156 assert_eq!((*i).as_value(), *s);
157 }
158 }
159}