nextcloud_ocs/query/
query_string.rs1use super::OcsFormat;
2use super::encoding::encode_component;
3
4#[derive(Clone, Debug, Default, PartialEq, Eq)]
10pub struct Query {
11 params: Vec<(String, String)>,
12}
13
14impl Query {
15 pub const fn new() -> Self {
17 Self { params: Vec::new() }
18 }
19
20 #[must_use]
25 pub fn param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
26 let key = key.into();
27 let value = value.into();
28 match self.params.iter_mut().find(|(name, _)| name == &key) {
29 Some(entry) => entry.1 = value,
30 None => self.params.push((key, value)),
31 }
32 self
33 }
34
35 #[must_use]
40 pub fn format(self, format: OcsFormat) -> Self {
41 self.param(OcsFormat::PARAM, format.as_str())
42 }
43
44 pub fn len(&self) -> usize {
46 self.params.len()
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.params.is_empty()
52 }
53
54 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
56 self.params
57 .iter()
58 .map(|(key, value)| (key.as_str(), value.as_str()))
59 }
60
61 pub fn build(&self) -> String {
66 let mut out = String::new();
67 for (key, value) in &self.params {
68 out.push(if out.is_empty() { '?' } else { '&' });
69 out.push_str(&encode_component(key));
70 out.push('=');
71 out.push_str(&encode_component(value));
72 }
73 out
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::{OcsFormat, Query};
80
81 #[test]
82 fn empty_query_renders_nothing() {
83 let query = Query::new();
84 assert!(query.is_empty());
85 assert_eq!(query.build(), "");
86 }
87
88 #[test]
89 fn params_are_joined_with_ampersands() {
90 let query = Query::new()
91 .param("limit", "5")
92 .param("offset", "10")
93 .format(OcsFormat::Json);
94 assert_eq!(query.build(), "?limit=5&offset=10&format=json");
95 assert_eq!(query.len(), 3);
96 }
97
98 #[test]
99 fn xml_format_is_selectable() {
100 assert_eq!(Query::new().format(OcsFormat::Xml).build(), "?format=xml");
101 }
102
103 #[test]
104 fn repeated_param_replaces_the_earlier_value() {
105 let query = Query::new().param("limit", "5").param("limit", "50");
106 assert_eq!(query.len(), 1);
107 assert_eq!(query.build(), "?limit=50");
108 }
109
110 #[test]
111 fn values_are_percent_encoded() {
112 let query = Query::new().param("search", "a&b=c d");
113 assert_eq!(query.build(), "?search=a%26b%3Dc%20d");
114 }
115
116 #[test]
117 fn iterators_yield_raw_values() {
118 let query = Query::new().param("user", "a b");
119 let pairs: Vec<_> = query.iter().collect();
120 assert_eq!(pairs, vec![("user", "a b")]);
121 }
122}