use std::borrow::Cow;
use std::{fmt, iter, str};
use tinyvec::TinyVec;
use super::encoding::{decode, encode, Kind};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Query<'a> {
inner: TinyVec<[Param<'a>; 4]>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct Param<'a> {
key: Cow<'a, str>,
value: Cow<'a, str>,
}
impl<'a> Query<'a> {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn get<K>(&self, key: K) -> Option<&str>
where
K: AsRef<str>,
{
self.inner.iter().find_map(|param| {
(param.key == key.as_ref()).then_some(param.value.as_ref())
})
}
pub fn get_all<K>(&self, key: K) -> impl Iterator<Item = &str>
where
K: AsRef<str>,
{
self.inner.iter().filter_map(move |param| {
(param.key == key.as_ref()).then_some(param.value.as_ref())
})
}
pub fn contains<K>(&self, key: K) -> bool
where
K: AsRef<str>,
{
self.inner.iter().any(|param| param.key == key.as_ref())
}
pub fn add<K, V>(&mut self, key: K, value: V)
where
K: Into<Cow<'a, str>>,
V: Into<Cow<'a, str>>,
{
self.inner.push(Param {
key: key.into(),
value: value.into(),
});
}
pub fn remove<K>(&mut self, key: K)
where
K: AsRef<str>,
{
self.inner.retain(|param| param.key != key.as_ref());
}
}
#[allow(clippy::must_use_candidate)]
impl Query<'_> {
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl<'a> From<&'a str> for Query<'a> {
#[allow(clippy::missing_panics_doc)]
fn from(value: &'a str) -> Self {
let mut pairs = Vec::new();
let mut start = 0;
let mut index = 0;
let chars = value.char_indices();
for (i, char) in chars.chain(iter::once((value.len(), '&'))) {
match char {
'=' if index == pairs.len() => {
pairs.push((decode(&value[start..i]), Cow::Borrowed("")));
start = i + 1;
}
'&' if start != i.saturating_sub(1) => {
if index < pairs.len() && pairs[index].1.is_empty() {
pairs[index].1 = decode(&value[start..i]);
} else {
pairs.push((
decode(&value[start..i]),
Cow::Borrowed(""),
));
}
start = i + 1;
index += 1;
}
_ => {}
}
}
Query::from_iter(pairs)
}
}
impl<'a, K, V> FromIterator<(K, V)> for Query<'a>
where
K: Into<Cow<'a, str>>,
V: Into<Cow<'a, str>>,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (K, V)>,
{
let mut query = Query::new();
for (key, value) in iter {
query.add(key, value);
}
query
}
}
impl fmt::Display for Query<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, param) in self.inner.iter().enumerate() {
if i > 0 {
f.write_str("&")?;
}
f.write_str(encode(¶m.key, Kind::Query).as_ref())?;
if !param.value.is_empty() {
f.write_str("=")?;
f.write_str(encode(¶m.value, Kind::Query).as_ref())?;
}
}
Ok(())
}
}