use crate::{Error, Result};
use core::{
default::Default,
fmt::{self, Display},
str::FromStr,
};
const ROUNDS_PARAM: &str = "rounds=";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Params {
pub(crate) rounds: u32,
}
impl Params {
pub const RECOMMENDED: Self = Self {
rounds: Self::RECOMMENDED_ROUNDS,
};
pub const RECOMMENDED_ROUNDS: u32 = 5_000;
pub const ROUNDS_MIN: u32 = 1_000;
pub const ROUNDS_MAX: u32 = 999_999_999;
pub fn new(rounds: u32) -> Result<Params> {
match rounds {
Self::ROUNDS_MIN..=Self::ROUNDS_MAX => Ok(Params { rounds }),
_ => Err(Error::RoundsInvalid),
}
}
}
impl Default for Params {
fn default() -> Self {
Params::RECOMMENDED
}
}
impl Display for Params {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "rounds={}", self.rounds)
}
}
impl FromStr for Params {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
if s.is_empty() {
return Ok(Self::default());
}
if let Some(rounds_str) = s.strip_prefix(ROUNDS_PARAM) {
Self::new(rounds_str.parse().map_err(|_| Error::RoundsInvalid)?)
} else {
Err(Error::ParamsInvalid)
}
}
}
#[cfg(test)]
mod tests {
use super::Params;
#[test]
fn test_sha256_crypt_invalid_rounds() {
let params = Params::new(Params::ROUNDS_MAX + 1);
assert!(params.is_err());
let params = Params::new(Params::ROUNDS_MIN - 1);
assert!(params.is_err());
}
#[test]
fn test_sha512_crypt_invalid_rounds() {
let params = Params::new(Params::ROUNDS_MAX + 1);
assert!(params.is_err());
let params = Params::new(Params::ROUNDS_MIN - 1);
assert!(params.is_err());
}
}