password_strength/
lib.rs

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