Skip to main content

passay_rs/rule/
length.rs

1use std::collections::HashMap;
2
3use crate::rule::rule_result::{CountCategory, RuleResult, RuleResultMetadata};
4use crate::rule::{PasswordData, Rule};
5
6pub const ERROR_CODE_MIN: &str = "TOO_SHORT";
7pub const ERROR_CODE_MAX: &str = "TOO_LONG";
8
9/// Rule for determining if a password is within a desired length. The minimum and maximum lengths are used inclusively
10/// to determine if a password meets this rule.
11///
12/// # Example
13///
14/// ```
15///  use passay_rs::rule::PasswordData;
16///  use passay_rs::rule::length::LengthRule;
17///  use passay_rs::rule::Rule;
18///
19///  let rule = LengthRule::new(4, 10);
20///  let password = PasswordData::with_password("123".to_string());
21///  let result = rule.validate(&password);
22///  assert!(!result.valid());
23/// ```
24pub struct LengthRule {
25    min_length: usize,
26    max_length: usize,
27}
28
29impl LengthRule {
30    pub fn new(min_length: usize, max_length: usize) -> Self {
31        Self {
32            min_length,
33            max_length,
34        }
35    }
36    pub fn with_exact_length(length: usize) -> Self {
37        Self {
38            min_length: length,
39            max_length: length,
40        }
41    }
42
43    fn create_rule_result_detail_parameters(&self) -> HashMap<String, String> {
44        let mut map = HashMap::new();
45        map.insert("min_length".to_string(), self.min_length.to_string());
46        map.insert("max_length".to_string(), self.max_length.to_string());
47        map
48    }
49    fn create_rule_result_metadata(password_data: &PasswordData) -> RuleResultMetadata {
50        RuleResultMetadata::new(CountCategory::Length, password_data.password.len())
51    }
52}
53
54impl Default for LengthRule {
55    fn default() -> Self {
56        LengthRule {
57            min_length: 0,
58            max_length: usize::MAX,
59        }
60    }
61}
62
63impl Rule for LengthRule {
64    fn validate(&self, password_data: &PasswordData) -> RuleResult {
65        let mut result = RuleResult::new(true);
66        let length = password_data.password.len();
67        if length < self.min_length {
68            result.add_error(
69                ERROR_CODE_MIN,
70                Some(self.create_rule_result_detail_parameters()),
71            );
72        } else if length > self.max_length {
73            result.add_error(
74                ERROR_CODE_MAX,
75                Some(self.create_rule_result_detail_parameters()),
76            )
77        }
78        result.set_metadata(Self::create_rule_result_metadata(password_data));
79        result
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use crate::rule::length::LengthRule;
86    use crate::rule::rule_result::CountCategory;
87    use crate::rule::{PasswordData, Rule};
88
89    #[test]
90    fn check_metada() {
91        let rule = LengthRule::new(4, 10);
92        let password = PasswordData::with_password("123".to_string());
93        let result = rule.validate(&password);
94        assert!(!result.valid());
95    }
96    #[test]
97    fn check_metadata() {
98        let rule = LengthRule::new(4, 10);
99        let result = rule.validate(&PasswordData::with_password("metadata".to_string()));
100        assert!(result.valid());
101        assert_eq!(8, result.metadata().get_count(CountCategory::Length));
102
103        let result = rule.validate(&PasswordData::with_password("md".to_string()));
104        assert!(!result.valid());
105        assert_eq!(2, result.metadata().get_count(CountCategory::Length));
106    }
107}