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}