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}