extern crate urlencoding;
use urlencoding::{encode, decode};
use std::iter::Iterator;
#[derive(Clone, Debug, PartialEq)]
pub struct QString {
keys: Vec<String>,
vals: Vec<Option<String>>,
empty: Option<String>,
}
fn new_esc(name: &str, value: &str) -> (String, String) {
(decode(name).unwrap_or_else(|_| name.to_string()),
decode(value).unwrap_or_else(|_| value.to_string()))
}
impl QString {
pub fn new<S>(params: Vec<(S, S)> ) -> QString
where S: Into<String> {
let mut keys = vec![];
let mut vals = vec![];
for (k, v) in params {
keys.push(k.into());
vals.push(Some(v.into()));
}
QString {
keys,
vals,
empty: None,
}
}
pub fn get(&self, name: &str) -> Option<String> {
let idx = self.keys.iter().position(|k| k == name)?;
self.vals[idx].clone()
}
pub fn to_pairs(self) -> Vec<(String, String)> {
let v: Vec<String> = self.vals
.into_iter()
.map(|v| v.unwrap())
.collect();
let ret: Vec<(String,String)> = self.keys
.into_iter()
.zip(v.into_iter())
.collect();
ret
}
}
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(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(new_esc(name, value));
cur = newcur;
}
QString::new(params)
}
}
impl<'b> ::std::ops::Index<&'static str> for QString {
type Output = Option<String>;
fn index<'a>(&'a self, index: &'b str) -> &'a Self::Output {
let idx = self.keys.iter()
.rev()
.position(|k| k == index);
let ret = match idx {
None => &self.empty,
Some(idx) => self.vals.index(idx),
};
ret
}
}
impl IntoIterator for QString {
type Item = (String, String);
type IntoIter = ::std::vec::IntoIter<(String,String)>;
fn into_iter(self) -> Self::IntoIter {
self.to_pairs().into_iter()
}
}
impl ::std::fmt::Display for QString {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
write!(f, "?")?;
for (idx, k) in self.keys.iter().enumerate() {
let v = self.vals[idx].as_ref().unwrap();
write!(f, "{}{}={}", (if idx == 0 {""} else {"&"}), encode(k), encode(v))?;
}
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);
let ps: Vec<(String, String)> = qs.to_pairs();
let cs: Vec<(String, String)> = ($result as Vec<(&str, &str)>)
.into_iter().map(|(k,v)| (k.to_string(), v.to_string()))
.collect();
assert_eq!(ps, cs);
}
)
}
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")]);
}