constrained_inputs/
constraints.rs

1//! Module with constraint configs to apply to inputs
2//! 
3//! This module provides various constraints that can be applied to user inputs,
4//! including constraints for strings and numbers. These constraints help ensure that
5//! input data meets specific criteria before it is accepted.
6//! 
7//! # Example
8//! 
9//! ```
10//! use constrained_inputs::constraints::{StringConstraint, NumberConstraint, Constraint};
11//! 
12//! fn main() {
13//!     let string_constraint = StringConstraint {
14//!         max_len: Some(10),
15//!         min_len: Some(5),
16//!         blacklist_chars: vec!['a', 'e', 'i', 'o', 'u'],
17//!     };
18//! 
19//!     let result = string_constraint.validate(&"hello");
20//!     assert_eq!(result, ConstraintResult::Err(ConstraintError::BlacklistedChar));
21//! 
22//!     let number_constraint = NumberConstraint {
23//!         max: Some(100),
24//!         min: Some(10),
25//!     };
26//! 
27//!     let result = number_constraint.validate(&50);
28//!     assert_eq!(result, ConstraintResult::Valid);
29//! }
30//! ```
31
32/// Result type for constraints
33#[derive(Debug, PartialEq)]
34pub enum ConstraintResult {
35    Valid,
36    Err(ConstraintError),
37}
38
39/// Error types for constraints
40#[derive(Debug, PartialEq)]
41pub enum ConstraintError {
42    InvalidConstraint,
43    TooLarge,
44    TooSmall,
45    BlacklistedChar,
46    TooLong,
47    TooShort,
48}
49
50/// Constraint trait
51pub trait Constraint<T> {
52    /// Validates data to make sure it follows constraints
53    fn validate(&self, data: &T) -> ConstraintResult;
54}
55
56/// String constraint config for applying constraints to what a string can be
57pub struct StringConstraint {
58    pub max_len: Option<usize>,
59    pub min_len: Option<usize>,
60    pub blacklist_chars: Vec<char>,
61}
62
63/// Implementing method to apply constraints on strings
64impl<T> Constraint<T> for StringConstraint
65where
66    T: AsRef<str>,
67{
68    fn validate(&self, data: &T) -> ConstraintResult {
69        let data = data.as_ref();
70
71        if let Some(max_len) = self.max_len {
72            if data.len() > max_len {
73                return ConstraintResult::Err(ConstraintError::TooLong);
74            }
75        }
76        if let Some(min_len) = self.min_len {
77            if data.len() < min_len {
78                return ConstraintResult::Err(ConstraintError::TooShort);
79            }
80        }
81        for ch in data.chars() {
82            if self.blacklist_chars.contains(&ch) {
83                return ConstraintResult::Err(ConstraintError::BlacklistedChar);
84            }
85        }
86
87        ConstraintResult::Valid
88    }
89}
90
91/// Number constraint config for applying constraints onto some number
92pub struct NumberConstraint {
93    pub max: Option<i32>,
94    pub min: Option<i32>,
95}
96
97/// Implementing method to apply constraints on numbers
98impl<T> Constraint<T> for NumberConstraint
99where
100    T: Into<i32> + Clone + PartialOrd,
101{
102    fn validate(&self, data: &T) -> ConstraintResult {
103        let res = i32::try_from(data.clone()).map_err(|_| ConstraintError::InvalidConstraint);
104        if let Err(err) = res {
105            return ConstraintResult::Err(err);
106        }
107        let data = res.unwrap();
108
109        if let Some(max) = self.max {
110            if data > max {
111                return ConstraintResult::Err(ConstraintError::TooLarge);
112            }
113        }
114        if let Some(min) = self.min {
115            if data < min {
116                return ConstraintResult::Err(ConstraintError::TooSmall);
117            }
118        }
119
120        ConstraintResult::Valid
121    }
122}