1use crate::{Error, Result};
4use core::num::NonZeroU32;
5
6#[cfg(feature = "password-hash")]
7use password_hash::{errors::InvalidValue, ParamsString, PasswordHash};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct Params {
14 pub s_cost: NonZeroU32,
16 pub t_cost: NonZeroU32,
18 pub p_cost: NonZeroU32,
22}
23
24impl Params {
25 pub const DEFAULT_S_COST: u32 = 1024;
27
28 pub const DEFAULT_T_COST: u32 = 3;
30
31 pub const DEFAULT_P_COST: u32 = 1;
33
34 pub fn new(s_cost: u32, t_cost: u32, p_cost: u32) -> Result<Self> {
36 Ok(Self {
37 s_cost: NonZeroU32::new(s_cost).ok_or(Error::MemoryTooLittle)?,
38 t_cost: NonZeroU32::new(t_cost).ok_or(Error::TimeTooSmall)?,
39 p_cost: NonZeroU32::new(p_cost).ok_or(Error::ThreadsTooFew)?,
40 })
41 }
42}
43
44impl Default for Params {
45 fn default() -> Self {
46 Self::new(
47 Self::DEFAULT_S_COST,
48 Self::DEFAULT_T_COST,
49 Self::DEFAULT_P_COST,
50 )
51 .unwrap()
52 }
53}
54
55#[cfg(feature = "password-hash")]
56#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
57impl<'a> TryFrom<&'a PasswordHash<'a>> for Params {
58 type Error = password_hash::Error;
59
60 fn try_from(hash: &'a PasswordHash<'a>) -> password_hash::Result<Self> {
61 let mut params = Self::default();
62
63 for (ident, value) in hash.params.iter() {
64 match ident.as_str() {
65 "s" => {
66 params.s_cost = NonZeroU32::new(value.decimal()?)
67 .ok_or_else(|| InvalidValue::TooShort.param_error())?;
68 }
69 "t" => {
70 params.t_cost = NonZeroU32::new(value.decimal()?)
71 .ok_or_else(|| InvalidValue::TooShort.param_error())?;
72 }
73 "p" => {
74 params.p_cost = NonZeroU32::new(value.decimal()?)
75 .ok_or_else(|| InvalidValue::TooShort.param_error())?;
76 }
77 _ => return Err(password_hash::Error::ParamNameInvalid),
78 }
79 }
80
81 Ok(params)
82 }
83}
84
85#[cfg(feature = "password-hash")]
86#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
87impl TryFrom<Params> for ParamsString {
88 type Error = password_hash::Error;
89
90 fn try_from(params: Params) -> password_hash::Result<ParamsString> {
91 ParamsString::try_from(¶ms)
92 }
93}
94
95#[cfg(feature = "password-hash")]
96#[cfg_attr(docsrs, doc(cfg(feature = "password-hash")))]
97impl TryFrom<&Params> for ParamsString {
98 type Error = password_hash::Error;
99
100 fn try_from(params: &Params) -> password_hash::Result<ParamsString> {
101 let mut output = ParamsString::new();
102 output.add_decimal("s", params.s_cost.get())?;
103 output.add_decimal("t", params.t_cost.get())?;
104 output.add_decimal("p", params.p_cost.get())?;
105
106 Ok(output)
107 }
108}