1use crate::{Error, Result};
4use core::{
5 default::Default,
6 fmt::{self, Display},
7 str::FromStr,
8};
9
10const ROUNDS_PARAM: &str = "rounds=";
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct Params {
16 pub(crate) rounds: u32,
18}
19
20impl Params {
21 pub const RECOMMENDED: Self = Self {
23 rounds: Self::RECOMMENDED_ROUNDS,
24 };
25
26 pub const RECOMMENDED_ROUNDS: u32 = 5_000;
28
29 pub const ROUNDS_MIN: u32 = 1_000;
31
32 pub const ROUNDS_MAX: u32 = 999_999_999;
34
35 pub fn new(rounds: u32) -> Result<Params> {
37 match rounds {
38 Self::ROUNDS_MIN..=Self::ROUNDS_MAX => Ok(Params { rounds }),
39 _ => Err(Error::RoundsInvalid),
40 }
41 }
42}
43
44impl Default for Params {
45 fn default() -> Self {
46 Params::RECOMMENDED
47 }
48}
49
50impl Display for Params {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "rounds={}", self.rounds)
53 }
54}
55
56impl FromStr for Params {
57 type Err = Error;
58
59 fn from_str(s: &str) -> Result<Self> {
60 if s.is_empty() {
61 return Ok(Self::default());
62 }
63
64 if let Some(rounds_str) = s.strip_prefix(ROUNDS_PARAM) {
65 Self::new(rounds_str.parse().map_err(|_| Error::RoundsInvalid)?)
66 } else {
67 Err(Error::ParamsInvalid)
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::Params;
75
76 #[test]
77 fn test_sha256_crypt_invalid_rounds() {
78 let params = Params::new(Params::ROUNDS_MAX + 1);
79 assert!(params.is_err());
80
81 let params = Params::new(Params::ROUNDS_MIN - 1);
82 assert!(params.is_err());
83 }
84
85 #[test]
86 fn test_sha512_crypt_invalid_rounds() {
87 let params = Params::new(Params::ROUNDS_MAX + 1);
88 assert!(params.is_err());
89
90 let params = Params::new(Params::ROUNDS_MIN - 1);
91 assert!(params.is_err());
92 }
93}