extern crate urlencoding;
use urlencoding::{encode, decode};
#[derive(Clone, Debug, PartialEq)]
pub struct QString {
pub params: Vec<Param>,
empty: String,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Param {
pub name: String,
pub value: String,
}
impl Param {
pub fn new(name: &str, value: &str) -> Param {
Param {
name: name.to_string(),
value: value.to_string(),
}
}
pub fn new_esc(name: &str, value: &str) -> Param {
Param {
name: decode(name).unwrap_or_else(|_| name.to_string()),
value: decode(value).unwrap_or_else(|_| value.to_string()),
}
}
}
impl QString {
pub fn new(params: Vec<Param> ) -> QString {
QString {
params: params,
empty: "".to_string(),
}
}
}
impl<'a> From<&'a str> for QString {
fn from(origin: &str) -> Self {
let mut cur = origin;
if cur.len() > 0 && &cur[0..1] == "?" {
cur = &cur[1..];
}
let mut params = vec![];
while cur.len() > 0 {
if &cur[0..1] == "&" {
cur = &cur[1..];
continue;
}
let (name, rest) = match cur.find("=") {
None => match cur.find("&") {
None => (cur, ""),
Some(pos) => {
params.push(Param::new_esc(&cur[..pos], ""));
cur = &cur[(pos + 1)..];
continue;
},
},
Some(pos) => (&cur[..pos], &cur[(pos + 1)..]),
};
if name.len() == 0 {
cur = rest;
continue;
}
let (value, newcur) = match rest.find("&") {
None => (rest, ""),
Some(pos) => (&rest[..pos], &rest[(pos + 1)..]),
};
params.push(Param::new_esc(name, value));
cur = newcur;
}
QString::new(params)
}
}
impl IntoIterator for QString {
type Item = Param;
type IntoIter = ::std::vec::IntoIter<Param>;
fn into_iter(self) -> Self::IntoIter {
self.params.into_iter()
}
}
impl<'a> PartialEq<(&'a str, &'a str)> for Param {
fn eq(&self, other: &(&str, &str)) -> bool {
self.name == other.0 && self.value == other.1
}
}
impl ::std::fmt::Display for Param {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "&{}={}", encode(&self.name), encode(&self.value))
}
}
impl<'b> ::std::ops::Index<&'b str> for QString {
type Output = String;
fn index(&self, index: &'b str) -> &Self::Output {
self.params.iter()
.rev()
.find(|p| p.name == index)
.map(|p| &p.value)
.unwrap_or(&self.empty)
}
}
impl ::std::fmt::Display for QString {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "?")?;
for param in &self.params {
write!(f, "{}", param)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test {
($func_name:ident, $origin:expr, $result:expr) => (
#[test]
fn $func_name() {
let qs = QString::from($origin);
assert_eq!(qs.params, $result as Vec<(&str, &str)>);
}
)
}
test!(empty_1, "", vec![]);
test!(empty_2, "?", vec![]);
test!(empty_3, "&", vec![]);
test!(empty_4, "=", vec![]);
test!(empty_5, "?=", vec![]);
test!(empty_6, "?&", vec![]);
test!(a_is_1, "a", vec![("a", "")]);
test!(a_is_2, "a=", vec![("a", "")]);
test!(a_is_3, "a=b", vec![("a", "b")]);
test!(a_is_4, "?a", vec![("a", "")]);
test!(a_is_5, "?a=", vec![("a", "")]);
test!(a_is_6, "?a=b", vec![("a", "b")]);
test!(a_is_7, "?&a", vec![("a", "")]);
test!(a_is_8, "?&a=", vec![("a", "")]);
test!(a_is_9, "?&a=b", vec![("a", "b")]);
test!(a_is_10, "?a=&", vec![("a", "")]);
test!(a_is_11, "?=a", vec![("a", "")]);
test!(a_is_eq_1, "a==", vec![("a", "=")]);
test!(is_q_1, "??", vec![("?", "")]);
test!(is_q_2, "&?", vec![("?", "")]);
test!(is_q_3, "??a", vec![("?a", "")]);
test!(is_q_4, "&?a", vec![("?a", "")]);
test!(ac_is_1, "?a&c", vec![("a", ""), ("c", "")]);
test!(ac_is_2, "?a&c&", vec![("a", ""), ("c", "")]);
test!(ac_is_3, "?a=&c", vec![("a", ""), ("c", "")]);
test!(ac_is_4, "?a=&c=", vec![("a", ""), ("c", "")]);
test!(ac_is_5, "?a=b&c=", vec![("a", "b"), ("c", "")]);
test!(ac_is_6, "?a=&c=d", vec![("a", ""), ("c", "d")]);
test!(ac_is_7, "?a=b&c=d", vec![("a", "b"), ("c", "d")]);
}