validate 0.6.0

A library to easily validate user input
Documentation
use super::{Rule, ValidationResult, Error};
use super::bound::Bound;
use regex::Regex;
use std::fmt::Display;

pub fn whitelist_chars(whitelist: &str) -> Rule<str> {
	let whitelist = whitelist.to_owned();
	Rule::from(move |input: &str| {
		for c in input.chars() {
			if !whitelist.contains(c) {
				return Err(format!("cannot contain the character '{}'", c).into());
			}
		}
		Ok(())
	})
}

pub fn regex(pattern: &str, description: &str) -> Rule<str> {
	let regex = Regex::new(pattern).unwrap();
	let description = description.to_owned();
	Rule::from(move |input: &str| {
		match regex.is_match(input) {
			true => Ok(()),
			false => Err(format!("must be {}", description).into())
		}
	})
}

pub fn email() -> Rule<str> {
	regex(r"^[^@]+@[^@]+\.[^@]+$", "a valid email")
}

pub fn bound<T, R>(bound: R) -> Rule<T>
	where T: Display + PartialOrd + PartialEq + 'static,
	      R: Into<Bound<T>> {
	let bound = bound.into();
	match bound {
		Bound::RangeTo(end) => Rule::from(move |input: &T| {
			if input < &end {
				Ok(())
			} else {
				Err(format!("must be < {}", end).into())
			}
		}),
		Bound::Range(start, end) => Rule::from(move |input: &T| {
			if input >= &start && input < &end {
				Ok(())
			} else {
				Err(format!("must be >= {} and < {}", start, end).into())
			}
		}),
		Bound::Exact(value) => Rule::from(move |input: &T| {
			if input == &value {
				Ok(())
			} else {
				Err(format!("must equal {}", value).into())
			}
		}),
		Bound::RangeFrom(start) => Rule::from(move |input: &T| {
			if input >= &start {
				Ok(())
			} else {
				Err(format!("must be >= {}", start).into())
			}
		})
	}
}

#[test]
fn validate_length() {
	let rule = bound(1..5).name("length").map(str::len);
	assert!(rule.validate("1234").is_ok());
	assert!(rule.validate("12345").is_err());
}


#[test]
fn validate_nest() {
	struct TestData {
		pub x: u32,
		pub y: String
	}

	impl TestData {
		fn get_x(&self) -> &u32 {
			&self.x
		}
	}

	let data = TestData {
		x: 1,
		y: "test".to_owned()
	};

	let rule = Rule::from(move |data: &TestData| {
				bound(0..5).name("x").validate(&data.x)?;
				bound(..5).name("y length").validate(&data.y.len())?;
		Ok(())
	});
	//
	assert!(rule.validate(&data).is_ok());
		assert_eq!(rule.validate(&TestData {
			x: 3,
			y: "too long".to_owned()
		}).unwrap_err().get_message(), "y length must be < 5".to_owned())
}


#[test]
fn test_whitelist() {
	let rule = whitelist_chars("0123456789");
	assert!(rule.validate("12").is_ok());
	assert!(rule.validate("00123456789").is_ok());
	assert!(rule.validate("asdf").is_err());
}

#[test]
fn test_regex() {
	let rule = regex("h.*h", "starts and ends with h");
	assert!(rule.validate(&"haah").is_ok());
	assert!(rule.validate(&"aargh").is_err());
}

#[test]
fn validate_email() {
	let rule = email();
	assert!(rule.validate(&"test@domain.com").is_ok());
	assert!(rule.validate(&"test123+123@domain.com").is_ok());
	assert!(rule.validate(&"test123.4@domain.com").is_ok());
	assert!(rule.validate(&"test1.2.3.4@domain.com").is_ok());

	assert!(rule.validate(&"test@domaincom").is_err());
	assert!(rule.validate(&"testdomain.com").is_err());
	assert!(rule.validate(&"@domaincom").is_err());
	assert!(rule.validate(&"@domain.com").is_err());
	assert!(rule.validate(&"test@").is_err());
	assert!(rule.validate(&"test").is_err());
}

#[test]
fn test_bound() {
	assert!(bound(..4).validate(&3).is_ok());
	assert!(bound(..4).validate(&4).is_err());
	assert!(bound(2..3).validate(&2).is_ok());
	assert!(bound(2..3).validate(&3).is_err());
	assert!(bound(2).validate(&2).is_ok());
	assert!(bound(2).validate(&3).is_err());
	assert!(bound(2..).validate(&2).is_ok());
	assert!(bound(2..).validate(&1).is_err());
}