use crate::{
HttpError,
helpers::{ iter_ext::IterExt, slice_ext::SliceExt },
data::{
Data,
encodings::{ Encoding, Uri, UriQuery }
}
};
use std::{
collections::HashMap, io::Write,
convert::{ TryFrom, TryInto }
};
use crate::helpers::slice_ext::ByteSliceExt;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct QueryString(HashMap<Data<UriQuery>, Data<UriQuery>>);
impl QueryString {
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn field(&self, key: &Data<UriQuery>) -> Option<&Data<UriQuery>> {
self.0.get(&key)
}
pub fn field_mut(&mut self, key: &Data<UriQuery>) -> Option<&mut Data<UriQuery>> {
self.0.get_mut(&key)
}
pub fn insert(&mut self, key: Data<UriQuery>, value: Data<UriQuery>) {
self.0.insert(key, value);
}
pub fn fields(&self) -> &HashMap<Data<UriQuery>, Data<UriQuery>> {
&self.0
}
pub fn fields_mut(&mut self) -> &mut HashMap<Data<UriQuery>, Data<UriQuery>> {
&mut self.0
}
pub fn to_string(&self) -> String {
let mut query = vec![b'?'];
self.0.iter().for_each(|(k, v)| match v.len() {
0 => write!(&mut query, "{}&", k).unwrap(),
_ => write!(&mut query, "{}={}&", k, v).unwrap()
});
let trimmed_len = query.trim_end_matches(|b| *b == b'&').len();
query.truncate(trimmed_len);
String::from_utf8(query).unwrap()
}
}
impl TryFrom<Data<Uri>> for QueryString {
type Error = HttpError;
fn try_from(uri: Data<Uri>) -> Result<Self, Self::Error> {
let query_part = match uri.splitn_pat(2, b"?").collect_min(2) {
Some(query_part) => query_part[1],
None => return Ok(Self::new())
};
let query_part = query_part.splitn_pat(2, b"#").next().unwrap();
if !UriQuery::is_valid(query_part) {
Err(HttpError::InvalidEncoding)?
}
let query_part = query_part.trim_end_matches(|b| *b == b'&');
if query_part.is_empty() {
return Ok(Self::new())
}
let mut query = HashMap::new();
for kv in query_part.split_pat(b"&") {
let kv: Vec<&[u8]> = kv.splitn_pat(2, b"=").collect();
match kv.len() {
1 => query.insert(kv[0].try_into()?, b"".as_ref().try_into()?),
2 => query.insert(kv[0].try_into()?, kv[1].try_into()?),
_ => unreachable!()
};
}
Ok(Self(query))
}
}