reinhardt_http/
path_params.rs1use std::collections::HashMap;
21
22#[derive(Debug, Clone, Default, PartialEq, Eq)]
44pub struct PathParams {
45 inner: Vec<(String, String)>,
46}
47
48impl PathParams {
49 pub fn new() -> Self {
51 Self { inner: Vec::new() }
52 }
53
54 pub fn len(&self) -> usize {
56 self.inner.len()
57 }
58
59 pub fn is_empty(&self) -> bool {
61 self.inner.is_empty()
62 }
63
64 pub fn get(&self, key: &str) -> Option<&String> {
69 self.inner.iter().find(|(k, _)| k == key).map(|(_, v)| v)
70 }
71
72 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) {
77 let key = key.into();
78 let value = value.into();
79 if let Some(slot) = self.inner.iter_mut().find(|(k, _)| *k == key) {
80 slot.1 = value;
81 } else {
82 self.inner.push((key, value));
83 }
84 }
85
86 pub fn iter(&self) -> std::slice::Iter<'_, (String, String)> {
88 self.inner.iter()
89 }
90
91 pub fn values(&self) -> impl Iterator<Item = &String> {
93 self.inner.iter().map(|(_, v)| v)
94 }
95
96 pub fn as_slice(&self) -> &[(String, String)] {
98 &self.inner
99 }
100
101 pub fn into_vec(self) -> Vec<(String, String)> {
103 self.inner
104 }
105}
106
107impl<K, V> FromIterator<(K, V)> for PathParams
108where
109 K: Into<String>,
110 V: Into<String>,
111{
112 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
113 let mut params = PathParams::new();
114 for (k, v) in iter {
115 params.insert(k, v);
116 }
117 params
118 }
119}
120
121impl IntoIterator for PathParams {
122 type Item = (String, String);
123 type IntoIter = std::vec::IntoIter<(String, String)>;
124
125 fn into_iter(self) -> Self::IntoIter {
126 self.inner.into_iter()
127 }
128}
129
130impl<'a> IntoIterator for &'a PathParams {
131 type Item = &'a (String, String);
132 type IntoIter = std::slice::Iter<'a, (String, String)>;
133
134 fn into_iter(self) -> Self::IntoIter {
135 self.inner.iter()
136 }
137}
138
139impl From<Vec<(String, String)>> for PathParams {
140 fn from(inner: Vec<(String, String)>) -> Self {
141 Self { inner }
143 }
144}
145
146impl From<HashMap<String, String>> for PathParams {
147 fn from(map: HashMap<String, String>) -> Self {
151 Self {
152 inner: map.into_iter().collect(),
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use rstest::rstest;
161
162 #[rstest]
163 fn insert_preserves_order() {
164 let mut params = PathParams::new();
166
167 params.insert("z", "first");
169 params.insert("a", "second");
170 params.insert("m", "third");
171
172 let order: Vec<&str> = params.iter().map(|(k, _)| k.as_str()).collect();
174 assert_eq!(order, vec!["z", "a", "m"]);
175 }
176
177 #[rstest]
178 fn get_finds_by_name() {
179 let mut params = PathParams::new();
181 params.insert("org", "myslug");
182 params.insert("cluster_id", "5");
183
184 let org = params.get("org");
186 let cluster_id = params.get("cluster_id");
187 let missing = params.get("missing");
188
189 assert_eq!(org.map(String::as_str), Some("myslug"));
191 assert_eq!(cluster_id.map(String::as_str), Some("5"));
192 assert_eq!(missing, None);
193 }
194
195 #[rstest]
196 fn insert_replaces_existing_in_place() {
197 let mut params = PathParams::new();
199 params.insert("a", "1");
200 params.insert("b", "2");
201
202 params.insert("a", "updated");
204
205 let collected: Vec<_> = params
207 .iter()
208 .map(|(k, v)| (k.as_str(), v.as_str()))
209 .collect();
210 assert_eq!(collected, vec![("a", "updated"), ("b", "2")]);
211 }
212
213 #[rstest]
214 fn from_vec_preserves_caller_order() {
215 let vec = vec![
217 ("org".to_string(), "myslug".to_string()),
218 ("cluster_id".to_string(), "5".to_string()),
219 ];
220
221 let params = PathParams::from(vec);
223
224 let order: Vec<&str> = params.iter().map(|(k, _)| k.as_str()).collect();
226 assert_eq!(order, vec!["org", "cluster_id"]);
227 }
228
229 #[rstest]
230 fn from_iter_collects_in_order() {
231 let pairs = vec![("z", "1"), ("a", "2")];
233
234 let params: PathParams = pairs.into_iter().collect();
236
237 let order: Vec<&str> = params.iter().map(|(k, _)| k.as_str()).collect();
239 assert_eq!(order, vec!["z", "a"]);
240 }
241}