use memchr::memchr2;
use http::RawStr;
pub struct FormItems<'f> {
string: &'f RawStr,
next_index: usize
}
impl<'f> FormItems<'f> {
#[inline]
pub fn completed(&self) -> bool {
self.next_index >= self.string.len()
}
#[inline]
pub fn exhaust(&mut self) -> bool {
while let Some(_) = self.next() { }
self.completed()
}
#[inline]
#[doc(hidden)]
pub fn mark_complete(&mut self) {
self.next_index = self.string.len()
}
#[inline]
pub fn inner_str(&self) -> &'f RawStr {
self.string
}
}
impl<'f> From<&'f RawStr> for FormItems<'f> {
#[inline(always)]
fn from(string: &'f RawStr) -> FormItems<'f> {
FormItems {
string: string,
next_index: 0
}
}
}
impl<'f> From<&'f str> for FormItems<'f> {
#[inline(always)]
fn from(string: &'f str) -> FormItems<'f> {
FormItems {
string: string.into(),
next_index: 0
}
}
}
impl<'f> Iterator for FormItems<'f> {
type Item = (&'f RawStr, &'f RawStr);
fn next(&mut self) -> Option<Self::Item> {
let s = &self.string[self.next_index..];
let (key, rest) = match memchr2(b'=', b'&', s.as_bytes()) {
Some(i) if s.as_bytes()[i] == b'=' => (&s[..i], &s[(i + 1)..]),
Some(_) => return None,
None => return None,
};
if key.is_empty() {
return None;
}
let (value, consumed) = match rest.find('&') {
Some(index) => (&rest[..index], index + 1),
None => (rest, rest.len()),
};
self.next_index += key.len() + 1 + consumed;
Some((key.into(), value.into()))
}
}
#[cfg(test)]
mod test {
use super::FormItems;
macro_rules! check_form {
(@bad $string:expr) => (check_form($string, None));
($string:expr, $expected:expr) => (check_form($string, Some($expected)));
}
fn check_form(string: &str, expected: Option<&[(&str, &str)]>) {
let mut items = FormItems::from(string);
let results: Vec<_> = items.by_ref().collect();
if let Some(expected) = expected {
assert_eq!(expected.len(), results.len());
for i in 0..results.len() {
let (expected_key, actual_key) = (expected[i].0, results[i].0);
let (expected_val, actual_val) = (expected[i].1, results[i].1);
assert!(actual_key == expected_key,
"key [{}] mismatch: expected {}, got {}",
i, expected_key, actual_key);
assert!(actual_val == expected_val,
"val [{}] mismatch: expected {}, got {}",
i, expected_val, actual_val);
}
} else {
assert!(!items.exhaust());
}
}
#[test]
fn test_form_string() {
check_form!("username=user&password=pass",
&[("username", "user"), ("password", "pass")]);
check_form!("user=user&user=pass",
&[("user", "user"), ("user", "pass")]);
check_form!("user=&password=pass",
&[("user", ""), ("password", "pass")]);
check_form!("a=b", &[("a", "b")]);
check_form!("value=Hello+World", &[("value", "Hello+World")]);
check_form!("user=", &[("user", "")]);
check_form!("user=&", &[("user", "")]);
check_form!("a=b&a=", &[("a", "b"), ("a", "")]);
check_form!(@bad "user=&password");
check_form!(@bad "user=x&&");
check_form!(@bad "a=b&a");
check_form!(@bad "=");
check_form!(@bad "&");
check_form!(@bad "=&");
check_form!(@bad "&=&");
check_form!(@bad "=&=");
}
}