diff --git a/lib/src/form.rs b/lib/src/form.rs new file mode 100644 index 00000000..b0b31f1d --- /dev/null +++ b/lib/src/form.rs @@ -0,0 +1,126 @@ +use std::str::FromStr; +use error::Error; + +pub trait FromForm<'f>: Sized { + fn from_form_string(s: &'f str) -> Result; +} + +pub trait FromFormValue<'v>: Sized { + type Error; + + fn parse(v: &'v str) -> Result; +} + +impl<'v> FromFormValue<'v> for &'v str { + type Error = Error; + + fn parse(v: &'v str) -> Result { + Ok(v) + } +} + +impl<'v> FromFormValue<'v> for isize { + type Error = &'v str; + + fn parse(v: &'v str) -> Result { + isize::from_str(v).map_err(|_| v) + } +} + +impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Option { + type Error = Error; + + fn parse(v: &'v str) -> Result { + match T::parse(v) { + Ok(v) => Ok(Some(v)), + Err(_) => Ok(None) + } + } +} + +// TODO: Add more useful implementations (range, regex, etc.). +impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Result { + type Error = Error; + + fn parse(v: &'v str) -> Result { + match T::parse(v) { + ok@Ok(_) => Ok(ok), + e@Err(_) => Ok(e) + } + } +} + +pub fn form_items<'f>(string: &'f str, items: &mut [(&'f str, &'f str)]) -> usize { + let mut param_num = 0; + let mut rest = string; + while rest.len() > 0 && param_num < items.len() { + let (key, remainder) = match rest.find('=') { + Some(index) => (&rest[..index], &rest[(index + 1)..]), + None => return param_num + }; + + rest = remainder; + let (value, remainder) = match rest.find('&') { + Some(index) => (&rest[..index], &rest[(index + 1)..]), + None => (rest, "") + }; + + rest = remainder; + items[param_num] = (key, value); + param_num += 1; + } + + param_num +} + +#[cfg(test)] +mod test { + use super::form_items; + + macro_rules! check_form { + ($string:expr, $expected: expr) => ({ + let mut output = Vec::with_capacity($expected.len()); + unsafe { output.set_len($expected.len()); } + + let results = output.as_mut_slice(); + assert_eq!($expected.len(), form_items($string, results)); + + 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!(expected_key == actual_key, + "key [{}] mismatch: expected {}, got {}", + i, expected_key, actual_key); + + assert!(expected_val == actual_val, + "val [{}] mismatch: expected {}, got {}", + i, expected_val, actual_val); + } + }) + } + + #[test] + fn test_form_string() { + let results = &[("username", "user"), ("password", "pass")]; + check_form!("username=user&password=pass", results); + + let results = &[("user", "user"), ("user", "pass")]; + check_form!("user=user&user=pass", results); + + let results = &[("user", ""), ("password", "pass")]; + check_form!("user=&password=pass", results); + + let results = &[("", ""), ("", "")]; + check_form!("=&=", results); + + let results = &[("a", "b")]; + check_form!("a=b", results); + + let results = &[("a", "b")]; + check_form!("a=b&a", results); + + let results = &[("a", "b"), ("a", "")]; + check_form!("a=b&a=", results); + } +}