use super::OcsFormat;
use super::encoding::encode_component;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Query {
params: Vec<(String, String)>,
}
impl Query {
pub const fn new() -> Self {
Self { params: Vec::new() }
}
#[must_use]
pub fn param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
let key = key.into();
let value = value.into();
match self.params.iter_mut().find(|(name, _)| name == &key) {
Some(entry) => entry.1 = value,
None => self.params.push((key, value)),
}
self
}
#[must_use]
pub fn format(self, format: OcsFormat) -> Self {
self.param(OcsFormat::PARAM, format.as_str())
}
pub fn len(&self) -> usize {
self.params.len()
}
pub fn is_empty(&self) -> bool {
self.params.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.params
.iter()
.map(|(key, value)| (key.as_str(), value.as_str()))
}
pub fn build(&self) -> String {
let mut out = String::new();
for (key, value) in &self.params {
out.push(if out.is_empty() { '?' } else { '&' });
out.push_str(&encode_component(key));
out.push('=');
out.push_str(&encode_component(value));
}
out
}
}
#[cfg(test)]
mod tests {
use super::{OcsFormat, Query};
#[test]
fn empty_query_renders_nothing() {
let query = Query::new();
assert!(query.is_empty());
assert_eq!(query.build(), "");
}
#[test]
fn params_are_joined_with_ampersands() {
let query = Query::new()
.param("limit", "5")
.param("offset", "10")
.format(OcsFormat::Json);
assert_eq!(query.build(), "?limit=5&offset=10&format=json");
assert_eq!(query.len(), 3);
}
#[test]
fn xml_format_is_selectable() {
assert_eq!(Query::new().format(OcsFormat::Xml).build(), "?format=xml");
}
#[test]
fn repeated_param_replaces_the_earlier_value() {
let query = Query::new().param("limit", "5").param("limit", "50");
assert_eq!(query.len(), 1);
assert_eq!(query.build(), "?limit=50");
}
#[test]
fn values_are_percent_encoded() {
let query = Query::new().param("search", "a&b=c d");
assert_eq!(query.build(), "?search=a%26b%3Dc%20d");
}
#[test]
fn iterators_yield_raw_values() {
let query = Query::new().param("user", "a b");
let pairs: Vec<_> = query.iter().collect();
assert_eq!(pairs, vec![("user", "a b")]);
}
}