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 ROUNDS_DEFAULT: u32 = 5_000;
23
24 pub const ROUNDS_MIN: u32 = 1_000;
26
27 pub const ROUNDS_MAX: u32 = 999_999_999;
29
30 pub fn new(rounds: u32) -> Result<Params> {
32 match rounds {
33 Self::ROUNDS_MIN..=Self::ROUNDS_MAX => Ok(Params { rounds }),
34 _ => Err(Error::RoundsInvalid),
35 }
36 }
37}
38
39impl Default for Params {
40 fn default() -> Self {
41 Params {
42 rounds: Self::ROUNDS_DEFAULT,
43 }
44 }
45}
46
47impl Display for Params {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 write!(f, "rounds={}", self.rounds)
50 }
51}
52
53impl FromStr for Params {
54 type Err = Error;
55
56 fn from_str(s: &str) -> Result<Self> {
57 if s.is_empty() {
58 return Ok(Self::default());
59 }
60
61 if let Some(rounds_str) = s.strip_prefix(ROUNDS_PARAM) {
62 Self::new(rounds_str.parse().map_err(|_| Error::RoundsInvalid)?)
63 } else {
64 Err(Error::ParamsInvalid)
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::Params;
72
73 #[test]
74 fn test_sha256_crypt_invalid_rounds() {
75 let params = Params::new(Params::ROUNDS_MAX + 1);
76 assert!(params.is_err());
77
78 let params = Params::new(Params::ROUNDS_MIN - 1);
79 assert!(params.is_err());
80 }
81
82 #[test]
83 fn test_sha512_crypt_invalid_rounds() {
84 let params = Params::new(Params::ROUNDS_MAX + 1);
85 assert!(params.is_err());
86
87 let params = Params::new(Params::ROUNDS_MIN - 1);
88 assert!(params.is_err());
89 }
90}