refined_type/rule/string/
regex.rs1pub use regex::Regex;
2
3#[macro_export]
4macro_rules! declare_regex_rule {
24 ($vis:vis $rule:ident, $regex:literal) => {
25 $crate::paste::item! {
26 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27 $vis struct $rule<STRING> {
28 _phantom: std::marker::PhantomData<STRING>,
29 }
30
31 impl<STRING: AsRef<str>> $crate::rule::Rule for $rule<STRING> {
32 type Item = STRING;
33
34 fn validate(target: Self::Item) -> Result<Self::Item, $crate::result::Error<Self::Item>> {
35 let target_as_ref = target.as_ref();
36 let regex = $crate::rule::Regex::new($regex).expect("invalid regex pattern");
37 if regex.is_match(target_as_ref) {
38 Ok(target)
39 } else {
40 let message = format!("\"{target_as_ref}\" does not match the regex pattern {regex}");
41 Err($crate::result::Error::new(target, message))
42 }
43 }
44 }
45 }
46 };
47 ($(($vis:vis $rule:ident, $regex:literal)),+) => {
48 $(declare_regex_rule!($rule, $regex);)+
49 };
50}
51
52#[cfg(test)]
53mod tests {
54 use crate::rule::Rule;
55
56 declare_regex_rule![
57 EmailRule,
58 r"^[a-zA-Z0-9_.+-]+@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$"
59 ];
60
61 declare_regex_rule![(FooRule, r"foo"), (BarRule, r"bar")];
62
63 #[test]
64 fn test_valid_email() {
65 let valid = "sample@example.com";
66 assert!(EmailRule::validate(&valid).is_ok())
67 }
68
69 #[test]
70 fn test_invalid_email() {
71 let invalid = "example.com";
72 assert!(EmailRule::validate(&invalid).is_err())
73 }
74
75 #[test]
76 fn test_valid_foo() {
77 let valid = "foo";
78 assert!(FooRule::validate(&valid).is_ok())
79 }
80
81 #[test]
82 fn test_invalid_foo() {
83 let invalid = String::from("bar");
84 assert!(FooRule::validate(&invalid).is_err())
85 }
86
87 #[test]
88 fn test_valid_bar() {
89 let valid = String::from("bar");
90 assert!(BarRule::validate(&valid).is_ok())
91 }
92
93 #[test]
94 fn test_invalid_bar() {
95 let invalid = String::from("foo");
96 assert!(BarRule::validate(&invalid).is_err())
97 }
98}