1use super::AsStr;
49use crate::error::Error;
50
51pub fn apply<T: Pattern, M: Matcher>(v: &T, (pat,): (&M,)) -> Result<(), Error> {
52 if !v.validate_pattern(pat) {
53 return Err(Error::new(format!(
54 "does not match pattern /{}/",
55 pat.as_str()
56 )));
57 }
58 Ok(())
59}
60
61pub trait Matcher: AsStr {
62 fn is_match(&self, haystack: &str) -> bool;
64}
65
66impl<T: Matcher> Matcher for std::sync::LazyLock<T> {
67 fn is_match(&self, haystack: &str) -> bool {
68 std::sync::LazyLock::force(self).is_match(haystack)
69 }
70}
71
72impl<T: AsStr> AsStr for std::sync::LazyLock<T> {
73 fn as_str(&self) -> &str {
74 std::sync::LazyLock::force(self).as_str()
75 }
76}
77
78pub trait Pattern {
79 fn validate_pattern<M: Matcher>(&self, matcher: &M) -> bool;
80}
81
82impl<T: AsStr> Pattern for T {
83 fn validate_pattern<M: Matcher>(&self, matcher: &M) -> bool {
84 matcher.is_match(self.as_str())
85 }
86}
87
88impl<T: Pattern> Pattern for Option<T> {
89 fn validate_pattern<M: Matcher>(&self, matcher: &M) -> bool {
90 match self {
91 Some(value) => value.validate_pattern(matcher),
92 None => true,
93 }
94 }
95}
96
97#[cfg(all(
98 feature = "regex",
99 feature = "js-sys",
100 target_arch = "wasm32",
101 target_os = "unknown"
102))]
103#[doc(hidden)]
104pub mod regex_js_sys {
105 pub use js_sys::RegExp;
106
107 use super::*;
108
109 impl Matcher for RegExp {
110 fn is_match(&self, haystack: &str) -> bool {
111 self.test(haystack)
112 }
113 }
114
115 impl AsStr for RegExp {
116 fn as_str(&self) -> &str {
117 "[Not supported in JS]"
118 }
119 }
120
121 pub struct SyncWrapper<T>(T);
122
123 impl<T> SyncWrapper<T> {
124 pub const unsafe fn new(inner: T) -> Self {
126 Self(inner)
127 }
128 }
129
130 impl<T: AsStr> AsStr for SyncWrapper<T> {
131 fn as_str(&self) -> &str {
132 self.0.as_str()
133 }
134 }
135
136 impl<T: Matcher> Matcher for SyncWrapper<T> {
137 fn is_match(&self, haystack: &str) -> bool {
138 self.0.is_match(haystack)
139 }
140 }
141
142 unsafe impl<T> Send for SyncWrapper<T> {}
143 unsafe impl<T> Sync for SyncWrapper<T> {}
144
145 pub type StaticPattern = std::sync::LazyLock<SyncWrapper<RegExp>>;
146
147 #[macro_export]
148 macro_rules! __init_js_sys_pattern {
149 ($pat:literal) => {
150 $crate::rules::pattern::regex_js_sys::StaticPattern::new(|| {
151 unsafe { $crate::rules::pattern::regex_js_sys::SyncWrapper::new(::js_sys::RegExp::new($pat, "u")) }
153 })
154 };
155 }
156 pub use crate::__init_js_sys_pattern as init_pattern;
157}
158
159#[cfg(feature = "regex")]
160#[doc(hidden)]
161pub mod regex {
162 pub use regex::Regex;
163
164 use super::{AsStr, Matcher};
165
166 impl Matcher for Regex {
167 fn is_match(&self, haystack: &str) -> bool {
168 self.is_match(haystack)
169 }
170 }
171
172 impl<T: Matcher> Matcher for once_cell::sync::Lazy<T> {
173 fn is_match(&self, haystack: &str) -> bool {
174 once_cell::sync::Lazy::force(self).is_match(haystack)
175 }
176 }
177
178 impl AsStr for Regex {
179 fn as_str(&self) -> &str {
180 self.as_str()
181 }
182 }
183
184 impl<T: AsStr> AsStr for once_cell::sync::Lazy<T> {
185 fn as_str(&self) -> &str {
186 once_cell::sync::Lazy::force(self).as_str()
187 }
188 }
189
190 pub type StaticPattern = std::sync::LazyLock<Regex>;
191
192 #[macro_export]
193 macro_rules! __init_pattern {
194 ($pat:literal) => {
195 $crate::rules::pattern::regex::StaticPattern::new(|| {
196 $crate::rules::pattern::regex::Regex::new($pat).unwrap()
197 })
198 };
199 }
200 pub use crate::__init_pattern as init_pattern;
201}