password_strength/
lib.rs

1pub mod analyzer;
2
3use crate::analyzer::Analyzer;
4use crate::analyzer::charset::CharsetAnalyzer;
5use crate::analyzer::length::LengthAnalyzer;
6use crate::analyzer::pattern::PatternAnalyzer;
7use crate::analyzer::trigraph::TrigraphAnalyzer;
8
9/// Estimates the overall password strength by combining scores from multiple analyzers
10/// using a direct weighted average.
11///
12/// This function calculates a weighted score based on length, character set diversity,
13/// trigraph randomness, and the pattern score (which reflects the absence of weak patterns).
14///
15/// # Arguments
16///
17/// * `password` - The password string slice to analyze.
18///
19/// # Returns
20///
21/// A score between 0.0 (very weak) and 1.0 (very strong) as an f32.
22///
23/// # Example Usage
24///
25/// ```
26/// use password_strength::estimate_strength;
27/// let password_weak = "password";
28/// let password_strong = "aB1!üöß字例😊_Long"; // Example of a potentially strong password
29///
30/// let strength_weak = estimate_strength(password_weak);
31/// let strength_strong = estimate_strength(password_strong);
32///
33/// println!("Strength of '{}': {:.3}", password_weak, strength_weak);
34/// println!("Strength of '{}': {:.3}", password_strong, strength_strong);
35/// ```
36pub fn estimate_strength(password: &str) -> f32 {
37    if password.is_empty() {
38        return 0.0;
39    }
40
41    let length_analyzer = LengthAnalyzer;
42    let charset_analyzer = CharsetAnalyzer;
43    let trigraph_analyzer = TrigraphAnalyzer;
44    let pattern_analyzer = PatternAnalyzer::default();
45
46    // Each score is between 0.0 and 1.0
47    let score_length = length_analyzer.analyze(password);
48    let score_charset = charset_analyzer.analyze(password);
49    let score_trigraph = trigraph_analyzer.analyze(password);
50    let score_pattern = pattern_analyzer.analyze(password);
51
52    // These weights determine the contribution of each aspect to the final score.
53    // They must sum to 1.0.
54    const WEIGHT_LENGTH: f32 = 0.25;
55    const WEIGHT_CHARSET: f32 = 0.25;
56    const WEIGHT_TRIGRAPH: f32 = 0.10;
57    const WEIGHT_PATTERN: f32 = 0.40;
58
59    // Direct weighted average of all four scores.
60    let final_score = (WEIGHT_LENGTH * score_length)
61        + (WEIGHT_CHARSET * score_charset)
62        + (WEIGHT_TRIGRAPH * score_trigraph)
63        + (WEIGHT_PATTERN * score_pattern);
64
65    // Since all scores are [0, 1] and weights sum to 1.0, the result
66    // should theoretically be [0, 1]. Clamping provides robustness.
67    final_score.clamp(0.0, 1.0)
68}