use super::combinators::Predicate;
#[derive(Clone, Copy, Default, Debug)]
pub struct NotEmpty;
impl Predicate<str> for NotEmpty {
#[inline]
fn check(&self, value: &str) -> bool {
!value.is_empty()
}
}
impl Predicate<String> for NotEmpty {
#[inline]
fn check(&self, value: &String) -> bool {
!value.is_empty()
}
}
pub fn not_empty() -> NotEmpty {
NotEmpty
}
#[derive(Clone, Copy, Debug)]
pub struct LenBetween {
min: usize,
max: usize,
}
impl Predicate<str> for LenBetween {
#[inline]
fn check(&self, value: &str) -> bool {
let len = value.len();
len >= self.min && len <= self.max
}
}
impl Predicate<String> for LenBetween {
#[inline]
fn check(&self, value: &String) -> bool {
let len = value.len();
len >= self.min && len <= self.max
}
}
pub fn len_between(min: usize, max: usize) -> LenBetween {
LenBetween { min, max }
}
pub fn len_min(min: usize) -> LenBetween {
LenBetween {
min,
max: usize::MAX,
}
}
pub fn len_max(max: usize) -> LenBetween {
LenBetween { min: 0, max }
}
pub fn len_eq(len: usize) -> LenBetween {
LenBetween { min: len, max: len }
}
#[derive(Clone, Debug)]
pub struct StartsWith<S>(pub S);
impl<S: AsRef<str> + Send + Sync> Predicate<str> for StartsWith<S> {
#[inline]
fn check(&self, value: &str) -> bool {
value.starts_with(self.0.as_ref())
}
}
impl<S: AsRef<str> + Send + Sync> Predicate<String> for StartsWith<S> {
#[inline]
fn check(&self, value: &String) -> bool {
value.starts_with(self.0.as_ref())
}
}
pub fn starts_with<S: AsRef<str> + Send + Sync>(prefix: S) -> StartsWith<S> {
StartsWith(prefix)
}
#[derive(Clone, Debug)]
pub struct EndsWith<S>(pub S);
impl<S: AsRef<str> + Send + Sync> Predicate<str> for EndsWith<S> {
#[inline]
fn check(&self, value: &str) -> bool {
value.ends_with(self.0.as_ref())
}
}
impl<S: AsRef<str> + Send + Sync> Predicate<String> for EndsWith<S> {
#[inline]
fn check(&self, value: &String) -> bool {
value.ends_with(self.0.as_ref())
}
}
pub fn ends_with<S: AsRef<str> + Send + Sync>(suffix: S) -> EndsWith<S> {
EndsWith(suffix)
}
#[derive(Clone, Debug)]
pub struct Contains<S>(pub S);
impl<S: AsRef<str> + Send + Sync> Predicate<str> for Contains<S> {
#[inline]
fn check(&self, value: &str) -> bool {
value.contains(self.0.as_ref())
}
}
impl<S: AsRef<str> + Send + Sync> Predicate<String> for Contains<S> {
#[inline]
fn check(&self, value: &String) -> bool {
value.contains(self.0.as_ref())
}
}
pub fn contains<S: AsRef<str> + Send + Sync>(substring: S) -> Contains<S> {
Contains(substring)
}
#[derive(Clone, Copy, Debug)]
pub struct AllChars<F>(pub F);
impl<F: Fn(char) -> bool + Send + Sync> Predicate<str> for AllChars<F> {
#[inline]
fn check(&self, value: &str) -> bool {
value.chars().all(&self.0)
}
}
impl<F: Fn(char) -> bool + Send + Sync> Predicate<String> for AllChars<F> {
#[inline]
fn check(&self, value: &String) -> bool {
value.chars().all(&self.0)
}
}
pub fn all_chars<F: Fn(char) -> bool + Send + Sync>(f: F) -> AllChars<F> {
AllChars(f)
}
#[derive(Clone, Copy, Debug)]
pub struct AnyChar<F>(pub F);
impl<F: Fn(char) -> bool + Send + Sync> Predicate<str> for AnyChar<F> {
#[inline]
fn check(&self, value: &str) -> bool {
value.chars().any(&self.0)
}
}
impl<F: Fn(char) -> bool + Send + Sync> Predicate<String> for AnyChar<F> {
#[inline]
fn check(&self, value: &String) -> bool {
value.chars().any(&self.0)
}
}
pub fn any_char<F: Fn(char) -> bool + Send + Sync>(f: F) -> AnyChar<F> {
AnyChar(f)
}
pub fn is_ascii() -> AllChars<fn(char) -> bool> {
AllChars(|c| c.is_ascii())
}
pub fn is_alphanumeric() -> AllChars<fn(char) -> bool> {
AllChars(|c| c.is_alphanumeric())
}
pub fn is_alphabetic() -> AllChars<fn(char) -> bool> {
AllChars(|c| c.is_alphabetic())
}
pub fn is_numeric() -> AllChars<fn(char) -> bool> {
AllChars(|c| c.is_numeric())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::predicate::{And, PredicateExt};
#[test]
fn test_not_empty() {
assert!(not_empty().check("hello"));
assert!(!not_empty().check(""));
}
#[test]
fn test_not_empty_string() {
assert!(not_empty().check(&String::from("hello")));
assert!(!not_empty().check(&String::new()));
}
#[test]
fn test_len_between() {
let p = len_between(3, 10);
assert!(!p.check("ab")); assert!(p.check("abc")); assert!(p.check("hello")); assert!(p.check("1234567890")); assert!(!p.check("12345678901")); }
#[test]
fn test_len_min() {
assert!(len_min(3).check("hello"));
assert!(len_min(3).check("abc"));
assert!(!len_min(3).check("ab"));
}
#[test]
fn test_len_max() {
assert!(len_max(5).check("hello"));
assert!(len_max(5).check("hi"));
assert!(!len_max(5).check("toolong"));
}
#[test]
fn test_len_eq() {
assert!(len_eq(5).check("hello"));
assert!(!len_eq(5).check("hi"));
assert!(!len_eq(5).check("toolong"));
}
#[test]
fn test_starts_with() {
assert!(starts_with("http").check("https://example.com"));
assert!(!starts_with("http").check("ftp://example.com"));
}
#[test]
fn test_ends_with() {
assert!(ends_with(".rs").check("main.rs"));
assert!(!ends_with(".rs").check("main.py"));
}
#[test]
fn test_contains() {
assert!(contains("@").check("user@example.com"));
assert!(!contains("@").check("invalid"));
}
#[test]
fn test_all_chars() {
assert!(all_chars(char::is_alphabetic).check("hello"));
assert!(!all_chars(char::is_alphabetic).check("hello123"));
}
#[test]
fn test_any_char() {
assert!(any_char(char::is_numeric).check("hello123"));
assert!(!any_char(char::is_numeric).check("hello"));
}
#[test]
fn test_is_ascii() {
assert!(is_ascii().check("hello"));
assert!(!is_ascii().check("héllo"));
}
#[test]
fn test_is_alphanumeric() {
assert!(is_alphanumeric().check("hello123"));
assert!(!is_alphanumeric().check("hello_123"));
}
#[test]
fn test_is_alphabetic() {
assert!(is_alphabetic().check("hello"));
assert!(!is_alphabetic().check("hello123"));
}
#[test]
fn test_is_numeric() {
assert!(is_numeric().check("123"));
assert!(!is_numeric().check("hello123"));
}
#[test]
fn test_complex_username_validation() {
let valid_username: And<And<NotEmpty, LenBetween>, AllChars<_>> = PredicateExt::<str>::and(
PredicateExt::<str>::and(not_empty(), len_between(3, 20)),
all_chars(|c: char| c.is_alphanumeric() || c == '_'),
);
assert!(valid_username.check("john_doe"));
assert!(valid_username.check("a_1"));
assert!(!valid_username.check("ab")); assert!(!valid_username.check("invalid-name")); }
}