1use std::borrow::Cow;
15
16use url::Url;
17
18use crate::error::BodyError;
19
20pub trait ParamValue<'a> {
22 #[allow(clippy::wrong_self_convention)]
23 fn as_value(&self) -> Cow<'a, str>;
25}
26
27impl ParamValue<'static> for bool {
28 fn as_value(&self) -> Cow<'static, str> {
29 if *self {
30 "true".into()
31 } else {
32 "false".into()
33 }
34 }
35}
36
37impl<'a> ParamValue<'a> for &'a str {
38 fn as_value(&self) -> Cow<'a, str> {
39 (*self).into()
40 }
41}
42
43impl ParamValue<'static> for String {
44 fn as_value(&self) -> Cow<'static, str> {
45 self.clone().into()
46 }
47}
48
49impl<'a> ParamValue<'a> for &'a String {
50 fn as_value(&self) -> Cow<'a, str> {
51 (*self).into()
52 }
53}
54
55impl<'a> ParamValue<'a> for Cow<'a, str> {
56 fn as_value(&self) -> Cow<'a, str> {
57 self.clone()
58 }
59}
60
61impl<'a, 'b: 'a> ParamValue<'a> for &'b Cow<'a, str> {
62 fn as_value(&self) -> Cow<'a, str> {
63 (*self).clone()
64 }
65}
66
67impl ParamValue<'static> for u64 {
68 fn as_value(&self) -> Cow<'static, str> {
69 format!("{}", self).into()
70 }
71}
72
73impl ParamValue<'static> for f64 {
74 fn as_value(&self) -> Cow<'static, str> {
75 format!("{}", self).into()
76 }
77}
78
79#[derive(Debug, Default, Clone)]
81pub struct FormParams<'a> {
82 params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
83}
84
85impl<'a> FormParams<'a> {
86 pub fn push<'b, K, V>(&mut self, key: K, value: V) -> &mut Self
88 where
89 K: Into<Cow<'a, str>>,
90 V: ParamValue<'b>,
91 'b: 'a,
92 {
93 self.params.push((key.into(), value.as_value()));
94 self
95 }
96
97 pub fn push_opt<'b, K, V>(&mut self, key: K, value: Option<V>) -> &mut Self
99 where
100 K: Into<Cow<'a, str>>,
101 V: ParamValue<'b>,
102 'b: 'a,
103 {
104 if let Some(value) = value {
105 self.params.push((key.into(), value.as_value()));
106 }
107 self
108 }
109
110 pub fn extend<'b, I, K, V>(&mut self, iter: I) -> &mut Self
112 where
113 I: Iterator<Item = (K, V)>,
114 K: Into<Cow<'a, str>>,
115 V: ParamValue<'b>,
116 'b: 'a,
117 {
118 self.params
119 .extend(iter.map(|(key, value)| (key.into(), value.as_value())));
120 self
121 }
122
123 pub fn into_body(self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
125 let body = serde_urlencoded::to_string(self.params)?;
126 Ok(Some((
127 "application/x-www-form-urlencoded",
128 body.into_bytes(),
129 )))
130 }
131}
132
133#[derive(Debug, Default, Clone)]
135pub struct QueryParams<'a> {
136 params: Vec<(Cow<'a, str>, Cow<'a, str>)>,
137}
138
139impl<'a> QueryParams<'a> {
140 pub fn push<'b, K, V>(&mut self, key: K, value: V) -> &mut Self
142 where
143 K: Into<Cow<'a, str>>,
144 V: ParamValue<'b>,
145 'b: 'a,
146 {
147 self.params.push((key.into(), value.as_value()));
148 self
149 }
150
151 pub fn push_opt<'b, K, V>(&mut self, key: K, value: Option<V>) -> &mut Self
153 where
154 K: Into<Cow<'a, str>>,
155 V: ParamValue<'b>,
156 'b: 'a,
157 {
158 if let Some(value) = value {
159 self.params.push((key.into(), value.as_value()));
160 }
161 self
162 }
163
164 pub fn extend<'b, I, K, V>(&mut self, iter: I) -> &mut Self
166 where
167 I: Iterator<Item = (K, V)>,
168 K: Into<Cow<'a, str>>,
169 V: ParamValue<'b>,
170 'b: 'a,
171 {
172 self.params
173 .extend(iter.map(|(key, value)| (key.into(), value.as_value())));
174 self
175 }
176
177 pub fn add_to_url(&self, url: &mut Url) {
179 let mut pairs = url.query_pairs_mut();
180 pairs.extend_pairs(self.params.iter());
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn bool_str() {
190 let items = &[(true, "true"), (false, "false")];
191
192 for (i, s) in items {
193 assert_eq!((*i).as_value(), *s);
194 }
195 }
196}