swift_check/require.rs
1//! Ensure each requirement was met
2//!
3//! **Note**:
4//! This module is experimental, other parts of this crate for the most part should have a stable
5//! api, this is an exception.
6//!
7//! The idea behind this part of the crate is you can define requirements once and reuse them
8//! across your project. A requirement is a higher-level concept than a condition in the context
9//! of this crate. Unlike the condition combinatorics `swift-check` exposes, requirements must have
10//! been fulfilled at least once in the input, or they raise the error associated with them. This is
11//! valuable for things like specifying password rules in a highly maintainable fashion.
12//!
13//! # Example
14//!
15//! ```
16//! use swift_check::{require::{Requirement, check}, requirement, requirements, eq};
17//!
18//! enum MyError {
19//! Space,
20//! Seven
21//! }
22//!
23//! requirement!(
24//! /// The input must include a space
25//! pub space => eq(b' ') =>! MyError::Space
26//! );
27//! requirement!(
28//! /// The input must include a seven
29//! pub seven => eq(b'7') =>! MyError::Seven
30//! );
31//!
32//! // (you can use any condition exposed by swift-check, not just eq)
33//!
34//! // now check if all the requirements were fulfilled, and that each byte in the input fell into
35//! // at least one requirement (for validation)
36//! let (valid, res) = check(
37//! b"example input 7",
38//! // as long as each requirement's error implements `Into<MyError>` you can use it.
39//! requirements!(MyError, [space, seven])
40//! ).result(); // fails fast, there's also `results()` which allows you to iterate over the results
41//!
42//! // not all the example input was 7 or space, so valid is false
43//! assert!(!valid);
44//!
45//! // there was a space and 7 in the input so the res is Ok
46//! assert!(res.is_ok())
47//! ```
48//!
49//! # Performance
50//!
51//! This is the slowest part of the api due to the added complexity to facilitate the desired
52//! functionality. It is not slow, but it is not swift, there's a lot of room for optimization so if
53//! `require` remains a part of the api it will overtime become more and more performant.
54
55use crate::arch;
56use crate::arch::{Vector, MoveMask};
57
58/// The default error type meant for when you're feeling lazy.
59#[repr(transparent)]
60pub struct ErrMsg {
61 pub msg: &'static str
62}
63
64impl ErrMsg {
65 #[inline] #[must_use]
66 pub const fn new(msg: &'static str) -> Self {
67 Self { msg }
68 }
69}
70
71impl From<&'static str> for ErrMsg {
72 #[inline]
73 fn from(value: &'static str) -> Self {
74 Self::new(value)
75 }
76}
77
78impl core::fmt::Display for ErrMsg {
79 #[inline]
80 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
81 f.write_str(self.msg)
82 }
83}
84
85impl core::fmt::Debug for ErrMsg {
86 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
87 write!(f, "Unsatisfied Requirement: {}", self.msg)
88 }
89}
90
91impl core::ops::Deref for ErrMsg {
92 type Target = str;
93
94 #[inline]
95 fn deref(&self) -> &Self::Target {
96 self.msg
97 }
98}
99
100#[cfg(feature = "std")]
101impl std::error::Error for ErrMsg {}
102
103/// A `Condition` is used internally by the [`Requirement`] trait. `requirement!` will expand into
104/// a constant function which returns a type that implements this trait.
105pub trait Condition {
106 /// The error type associated with the requirement. When used in `requirements` all the error
107 /// types must implement `Into` to a common type.
108 type Error;
109 /// Used internally by the `requirements!` macro.
110 #[must_use]
111 fn check(&mut self, vector: Vector) -> MoveMask;
112 /// Check that the condition was met at least once, used internally by the `requirements!` macro
113 fn ok(self) -> Result<(), Self::Error>;
114}
115
116/// A trait representing a collection of conditions which must be met at least once.
117///
118/// # Methods
119///
120/// * [`result`] - Check if all requirements were met, fails fast
121/// * [`results`] - Iterate over each requirement's result
122///
123
124/// [`result`]: Requirement::result
125/// [`results`]: Requirement::results
126pub trait Requirement {
127 /// The common error of each `requirement!`, each error associated with the requirement should
128 /// either be this error type or implement `Into` for this error type.
129 type Error;
130 /// Used internally by the [`check`] function
131 fn check(&mut self, vector: Vector);
132 /// Used internally by the [`check`] function
133 ///
134 /// This operates similarly to `Requirement::check` but for handling partial loads.
135 ///
136 /// The reason this is necessary is that partial loads pad the register with zeroes to ensure
137 /// safety, if these zeroes to not meet any requirement then the validator would consider there
138 /// to be illegal input and flag it as such.
139 ///
140 /// # Arguments
141 ///
142 /// * `vector` - The vector to check.
143 /// * `len` - The length of the data so that the validator knows what to check.
144 fn check_partial(&mut self, vector: Vector, len: u32);
145 /// # Result
146 ///
147 /// Get the result of the requirement check. This will return the first error caught in order
148 /// of the requirements. If you need to know each unfulfilled requirement see [`results`]
149 ///
150 /// # Returns
151 ///
152 /// 0. A `bool` denoting if all bytes met at least one of the requirements.
153 /// 1. `Ok(())` if all requirements were met, or the first error (in order of requirements)
154 /// caught.
155 ///
156
157 /// [`results`]: Requirement::results
158 fn result(self) -> (bool, Result<(), Self::Error>);
159 /// # Results
160 ///
161 /// Get an iterator over each requirement result. If you only need to know if the
162 /// requirements were fulfilled see [`result`].
163 ///
164 /// # Returns
165 ///
166 /// 0. A `bool` denoting if all bytes met at least one of the requirements.
167 /// 1. An iterator over results, in order of the provided requirements.
168 ///
169
170 /// [`result`]: Requirement::result
171 fn results(self) -> (bool, impl Iterator<Item = Result<(), Self::Error>>);
172}
173
174/// `Requires` is the final representation of a requirement, usable in the `requirements!` macro.
175///
176/// When you use the `requirement!` macro it will expand into a constant function which returns
177/// a `Requires` instance.
178///
179/// # Generics
180///
181/// - `C`: The required condition
182/// - `Raise`: If the requirement was not fulfilled this is invoked to raise the corresponding `Err`
183/// - `Err`: The error to `Raise` if the requirement was not fulfilled
184pub struct Requires<C, Raise, Err>
185 where
186 C: Fn(Vector) -> Vector,
187 Raise: FnOnce() -> Err
188{
189 /// The required condition
190 pub cond: C,
191 /// Raise the error if the requirement was not met
192 raise: Raise,
193 /// Track if the requirement has been met
194 seen: bool
195}
196
197impl<C, Raise, Err> Requires<C, Raise, Err>
198 where
199 C: Fn(Vector) -> Vector,
200 Raise: FnOnce() -> Err
201{
202 /// Create a new `Requires` instance
203 #[inline] #[must_use]
204 pub const fn new(cond: C, raise: Raise) -> Self {
205 Self { cond, raise, seen: false }
206 }
207}
208
209impl<C, Raise, Err> Condition for Requires<C, Raise, Err>
210 where
211 C: Fn(Vector) -> Vector,
212 Raise: FnOnce() -> Err
213{
214 type Error = Err;
215
216 /// Compute the condition over the vector, if any bit was set update seen to true.
217 ///
218 /// # Returns
219 ///
220 /// The `MoveMask` used to extract the condition result, used to check validity of the input,
221 /// ensuring each byte fulfilled at least one condition.
222 #[inline] #[must_use]
223 fn check(&mut self, vector: Vector) -> MoveMask {
224 let mask = unsafe { MoveMask::new((self.cond)(vector)) };
225 self.seen |= mask.any_bit_set();
226 mask
227 }
228
229 #[inline(always)]
230 fn ok(self) -> Result<(), Self::Error> {
231 if self.seen { Ok(()) } else { Err((self.raise)()) }
232 }
233}
234
235/// Define a Requirement
236///
237/// Requirements can easily be composed in the `requirements!` macro, allowing you to create
238/// highly maintainable and robust validators with decent performance.
239///
240/// # Example
241///
242/// ```
243/// use swift_check::{
244/// require::{Requirement, check},
245/// requirement, requirements,
246/// range, eq
247/// };
248///
249/// enum Errors {
250/// Space,
251/// F,
252/// Number,
253/// }
254///
255/// requirement!(
256/// /// The input must include a space
257/// space => eq(b' ') =>! Errors::Space
258/// );
259///
260/// requirement!(
261/// /// The input must include `F`
262/// f => eq(b'f') =>! Errors::F
263/// );
264///
265/// requirement!(
266/// /// The input must include a number
267/// number => range!(b'0'..=b'9') =>! Errors::Number
268/// );
269///
270/// // now we can use each requirement together in the requirements! macro, as long as the errors
271/// // of each requirement impl Into<requirements! error type> then you're good to go
272///
273/// let (valid, res_iter) = check(
274/// b"example input",
275/// requirements!(Errors, [space, f, number])
276/// ).results();
277///
278/// // valid denotes each byte met at least one of the requirements, in this case it should be
279/// // false
280/// assert!(!valid);
281///
282/// for result in res_iter {
283/// match result {
284/// Err(Errors::F) => {/* There was not an F in the input */},
285/// Err(Errors::Number) => {/* There was not a number in the input */},
286/// Err(Errors::Space) => unreachable!("There was a space in the input"),
287/// _ => {}
288/// }
289/// }
290/// ```
291///
292/// Or, if you're feeling lazy you can use literals as your error, these use the [`ErrMsg`] type.
293///
294/// ```
295/// # use swift_check::{
296/// # require::{Requirement, check},
297/// # requirement, requirements,
298/// # range, eq
299/// # };
300/// #
301/// requirement!(pub space => eq(b' ') =>! "There needs to be a space!");
302/// ```
303///
304/// # Syntax
305///
306/// ```txt
307/// #[attributes]
308/// visibility identifier => condition =>! error
309/// ```
310///
311/// **Syntax Limitation**: Right now you cannot use errors within a module, so you must import them.
312#[macro_export]
313macro_rules! requirement {
314 // when implemented as an accumulator it didn't work well with rust analyzer / rust rover
315 (
316 $(#[$attr:meta])*
317 $vis:vis $req_name:ident => $cond:expr =>! $error_message:literal
318 ) => {
319 $(#[$attr])*
320 #[must_use]
321 $vis const fn $req_name () -> impl $crate::require::Condition<Error = $crate::require::ErrMsg> {
322 let res = $crate::require::Requires::new($cond, || { $crate::require::ErrMsg::new($error_message) });
323 res
324 }
325 };
326 (
327 $(#[$attr:meta])*
328 $vis:vis $req_name:ident => $cond:expr =>! $create_err:expr => $err_ty:ty
329 ) => {
330 $(#[$attr])*
331 #[must_use]
332 $vis const fn $req_name () -> impl $crate::require::Condition<Error = $err_ty> {
333 let res = $crate::require::Requires::new($cond, || { $create_err });
334 res
335 }
336 };
337 (
338 $(#[$attr:meta])*
339 $vis:vis $req_name:ident => $cond:expr =>! $err:ident ($($args:expr),* $(,)?)
340 ) => {
341 $(#[$attr])*
342 #[must_use]
343 $vis const fn $req_name () -> impl $crate::require::Condition<Error = $err> {
344 let res = $crate::require::Requires::new($cond, || { $err ($($args),*) });
345 res
346 }
347 };
348 (
349 $(#[$attr:meta])*
350 $vis:vis $req_name:ident => $cond:expr =>! $err:ident :: $func:ident ($($args:expr),* $(,)?)
351 ) => {
352 $(#[$attr])*
353 #[must_use]
354 $vis const fn $req_name () -> impl $crate::require::Condition<Error = $err> {
355 let res = $crate::require::Requires::new($cond, || { $err :: $func ($($args),*) });
356 res
357 }
358 };
359 (
360 $(#[$attr:meta])*
361 $vis:vis $req_name:ident => $cond:expr =>! $err:ident :: $variant:ident
362 ) => {
363 $(#[$attr])*
364 #[must_use]
365 $vis const fn $req_name () -> impl $crate::require::Condition<Error = $err> {
366 let res = $crate::require::Requires::new($cond, || { $err :: $variant });
367 res
368 }
369 };
370}
371
372/// Check multiple `requirement!`s
373///
374/// # Example
375///
376/// ```
377/// use swift_check::{
378/// require::{Requirement, check},
379/// requirement, requirements,
380/// eq
381/// };
382///
383/// requirement!(pub space => eq(b' ') =>! "There needs to be a space!");
384/// requirement!(pub seven => eq(b'7') =>! "There needs to be a seven!");
385///
386/// let (valid, res) = check(
387/// b"example input 7",
388/// requirements!([space, seven])
389/// ).results();
390///
391/// // The first element of result denotes if all bytes met at least one of the conditions, in this
392/// // case this is false.
393/// assert!(!valid);
394///
395/// // the res is an iterator over each requirement! result. You also can use `result` instead of
396/// // `results` if you only need to know all requirements were met and care less about granular
397/// // error reporting.
398/// ```
399///
400/// # Error Handling
401///
402/// The individual requirements do not need to share the same error type, but they do all need to
403/// implement either `From` or `Into` the `requirements!` error type.
404///
405/// # Syntax
406///
407/// ```txt
408/// Error Type, [Requirements, ...]
409/// ```
410///
411/// or for when you're feeling lazy, the default error is `ErrMsg`, the above example uses this.
412///
413/// ```txt
414/// [Requirements, ...]
415/// ```
416#[macro_export]
417macro_rules! requirements {
418 ([$($requirement:ident),* $(,)?] $(,)?) => {
419 $crate::requirements!($crate::require::ErrMsg, [$($requirement),*])
420 };
421 ($error:ty, [$($requirement:ident),* $(,)?] $(,)?) => {{
422 #[allow(non_camel_case_types)]
423 struct Requirements<$($requirement: $crate::require::Condition),*> {
424 __valid: bool,
425 $($requirement: $requirement),*
426 }
427 #[allow(non_camel_case_types)]
428 impl<$($requirement),*> $crate::require::Requirement for Requirements<$($requirement),*>
429 where
430 $($requirement: $crate::require::Condition,
431 <$requirement as $crate::require::Condition>::Error: Into<$error>),*
432 {
433 type Error = $error;
434 #[inline]
435 fn check(&mut self, vector: $crate::arch::Vector) {
436 #[allow(unused_imports)]
437 use $crate::require::Condition as _;
438 self.__valid &= ($(self.$requirement.check(vector) )|*).all_bits_set();
439 }
440 #[inline]
441 fn check_partial(&mut self, vector: $crate::arch::Vector, len: u32) {
442 #[allow(unused_imports)]
443 use $crate::require::Condition as _;
444 self.__valid &= ($(self.$requirement.check(vector) )|*)
445 .trailing_ones() >= len;
446 }
447 #[inline]
448 fn result(self) -> (bool, Result<(), Self::Error>) {
449 $(
450 if let Err(err) = self.$requirement.ok() {
451 return (self.__valid, Err(err.into()));
452 };
453 )*
454 (self.__valid, Ok(()))
455 }
456 #[inline]
457 fn results(self) -> (bool, impl Iterator<Item=Result<(), Self::Error>>) {
458 (
459 self.__valid,
460 [$(self.$requirement.ok().map_err(|err| -> Self::Error {err.into()})),*]
461 .into_iter()
462 )
463 }
464 }
465
466 Requirements {
467 __valid: true,
468 $($requirement: $requirement ()),*
469 }
470 }};
471}
472
473/// Check that all `requirement!`s are fulfilled
474///
475/// # Arguments
476///
477/// * `data` - The data to validate
478/// * `req` - The requirements to check against
479///
480/// # Result
481///
482/// An implementor of [`Requirement`] at the final state
483///
484/// # Example
485///
486/// ```
487/// use swift_check::{
488/// require::{Requirement, check},
489/// requirement, requirements,
490/// eq
491/// };
492///
493/// requirement!(pub space => eq(b' ') =>! "There needs to be a space!");
494/// requirement!(pub seven => eq(b'7') =>! "There needs to be a seven!");
495///
496/// let (valid, res) = check(
497/// b"example input 7",
498/// requirements!([space, seven])
499/// ).results();
500///
501/// // The first element of result denotes if all bytes met at least one of the conditions, in this
502/// // case this is false.
503/// assert!(!valid);
504/// ```
505#[inline]
506pub fn check<R: Requirement>(data: &[u8], mut req: R) -> R {
507 if data.len() >= arch::WIDTH {
508 unsafe { arch::scan::ensure_requirements(data, req) }
509 } else {
510 let len = data.len();
511 req.check_partial(unsafe { arch::load_partial(data, len) }, len as u32);
512 req
513 }
514}
515
516#[cfg(test)]
517#[test]
518fn test() {
519 use crate::{eq, range};
520 struct SpecialError(&'static str);
521 impl From<SpecialError> for ErrMsg {
522 fn from(value: SpecialError) -> Self {
523 ErrMsg::new(value.0)
524 }
525 }
526 impl SpecialError {
527 fn new(msg: &'static str) -> Self {
528 Self ( msg )
529 }
530 }
531
532 // no std, no alloc, requirement checks / validator with SIMD on aarch64, x86, and WASM32
533
534 // if you're lazy you can use literals as your error
535 requirement!(
536 /// There should be at least one uppercase letter
537 #[inline] pub uppercase => range!(b'A'..=b'Z') =>! "needs uppercase!"
538 );
539 requirement!(
540 /// There should be at least one lowercase letter
541 #[inline] pub lowercase => range!(b'a'..=b'z') =>! "needs lowercase!"
542 );
543 // you can use multiple different error types as long as they impl From to the requirements! err
544 requirement!(
545 /// There needs to be at least one number
546 #[inline] pub numeric => range!(b'0'..=b'9') =>! SpecialError("needs number!")
547 );
548 requirement!(
549 /// Question marks are a requirement
550 #[inline] pub question_mark => eq(b'?') =>! SpecialError::new("needs a question mark!")
551 );
552
553 let res = check(
554 b"hello world 12345678910",
555 requirements!([uppercase, lowercase, numeric, question_mark])
556 );
557
558 // you can iterate through the errors
559 for res in res.results().1 {
560 if let Err(err) = res {
561 println!("{err:?}");
562 }
563 }
564
565 let res = check(
566 b"hello world 12345678910",
567 requirements!([uppercase, lowercase, numeric, question_mark])
568 );
569
570 // or if you just want to know if an err took place you can use ok
571 println!("{:?}", res.result().1.unwrap_err());
572}