gpui_form_num_regex/
lib.rs1use regex::Regex;
2use std::sync::OnceLock;
3
4pub trait NumRegex {
5 fn validation_regex() -> &'static Regex;
6}
7
8macro_rules! static_regex {
9 ($pattern:expr) => {{
10 static REGEX: OnceLock<Regex> = OnceLock::new();
11 REGEX.get_or_init(|| Regex::new($pattern).expect("Invalid regex pattern"))
12 }};
13}
14
15macro_rules! impl_numregex_signed {
16 ($($t:ty),*) => {
17 $(
18 impl NumRegex for $t {
19 fn validation_regex() -> &'static Regex {
20 static_regex!(r"^[+-]?(?:0|[1-9]\d*)$")
21 }
22 }
23 )*
24 };
25}
26
27macro_rules! impl_numregex_unsigned {
28 ($($t:ty),*) => {
29 $(
30 impl NumRegex for $t {
31 fn validation_regex() -> &'static Regex {
32 static_regex!(r"^(?:0|[1-9]\d*)$")
33 }
34 }
35 )*
36 };
37}
38
39macro_rules! impl_numregex_float {
40 ($($t:ty),*) => {
41 $(
42 impl NumRegex for $t {
43 fn validation_regex() -> &'static Regex {
44 static_regex!(r"^[+-]?(?:(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?|inf|infinity|nan)$")
46 }
47 }
48 )*
49 };
50}
51
52impl_numregex_signed!(i8, i16, i32, i64, i128, isize);
53impl_numregex_unsigned!(u8, u16, u32, u64, u128, usize);
54impl_numregex_float!(f32, f64);
55
56#[cfg(feature = "rust_decimal")]
57impl NumRegex for rust_decimal::Decimal {
58 fn validation_regex() -> &'static Regex {
59 static_regex!(r"^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$")
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_signed_integers() {
71 assert!(i32::validation_regex().is_match("123"));
72 assert!(i32::validation_regex().is_match("-123"));
73 assert!(i32::validation_regex().is_match("+123"));
74 assert!(i32::validation_regex().is_match("0"));
75 assert!(!i32::validation_regex().is_match("123.45"));
76 assert!(!i32::validation_regex().is_match("abc"));
77 assert!(!i32::validation_regex().is_match(""));
78 }
79
80 #[test]
81 fn test_unsigned_integers() {
82 assert!(u32::validation_regex().is_match("123"));
83 assert!(u32::validation_regex().is_match("0"));
84 assert!(!u32::validation_regex().is_match("-123"));
85 assert!(!u32::validation_regex().is_match("+123"));
86 assert!(!u32::validation_regex().is_match("123.45"));
87 }
88
89 #[test]
90 fn test_floats() {
91 assert!(f64::validation_regex().is_match("123.45"));
92 assert!(f64::validation_regex().is_match("-123.45"));
93 assert!(f64::validation_regex().is_match("1.23e10"));
94 assert!(f64::validation_regex().is_match("1.23E-10"));
95 assert!(f64::validation_regex().is_match("inf"));
96 assert!(f64::validation_regex().is_match("nan"));
97 assert!(f64::validation_regex().is_match("123"));
98 assert!(f64::validation_regex().is_match(".5"));
99 assert!(!f64::validation_regex().is_match("123."));
100 }
101
102 #[cfg(feature = "rust_decimal")]
103 #[test]
104 fn test_decimal() {
105 use rust_decimal::Decimal;
106
107 assert!(Decimal::validation_regex().is_match("123.45"));
108 assert!(Decimal::validation_regex().is_match("-123.45"));
109 assert!(Decimal::validation_regex().is_match("1.23e10"));
110 assert!(Decimal::validation_regex().is_match("0"));
111 assert!(!Decimal::validation_regex().is_match("inf"));
112 assert!(!Decimal::validation_regex().is_match("nan"));
113 }
114
115 #[test]
116 fn test_regex_compilation() {
117 let _ = i32::validation_regex();
118 let _ = u32::validation_regex();
119 let _ = f64::validation_regex();
120
121 #[cfg(feature = "rust_decimal")]
122 {
123 use rust_decimal::Decimal;
124 let _ = Decimal::validation_regex();
125 }
126 }
127}