use crate::cli::RupassArgs;
use crate::cli::UserInterface;
use crate::core::app_errors::{GenerationError, Result};
use crate::crypto::zxcvbn_wrapper::{MAX_PASSWORD_BYTES, MAX_PASSWORD_CHARS};
use fluent::FluentBundle;
use fluent::FluentResource;
use futures::FutureExt;
#[doc(alias = "length")]
#[doc(alias = "password length")]
pub async fn get_password_length(
ui: &mut dyn UserInterface,
bundle: &FluentBundle<FluentResource>,
args: &RupassArgs,
) -> Result<usize> {
if let Some(len) = args.password_length {
validate_password_length(len)?;
return Ok(len);
}
if args.no_prompt {
return Err(GenerationError::InvalidLength);
}
let prompt = crate::core::utils::fallback_translation(
bundle,
"question_password_length",
"Enter password length:",
None,
);
let too_short_msg = crate::core::utils::fallback_translation(
bundle,
"error_password_too_short",
"Password is too short.",
None,
);
let len = crate::core::utils::prompt_loop(ui, &prompt, parse_length_input, {
move |ui, _| {
let msg = too_short_msg.clone();
async move { if let Err(_e) = ui.print(&msg).await {} }.boxed_local()
}
})
.await?;
Ok(len)
}
#[doc(alias = "validate")]
#[doc(alias = "length validation")]
pub fn validate_password_length(len: usize) -> Result<()> {
if !(15..=MAX_PASSWORD_CHARS).contains(&len) {
Err(GenerationError::InvalidLength)
} else {
Ok(())
}
}
pub fn validate_password_byte_length(pwd: &str) -> Result<()> {
if pwd.len() > MAX_PASSWORD_BYTES {
Err(GenerationError::InvalidLength)
} else {
Ok(())
}
}
fn parse_length_input(s: &str) -> std::result::Result<usize, GenerationError> {
let v = s
.trim()
.parse::<usize>()
.map_err(|_| GenerationError::InvalidLength)?;
validate_password_length(v)?;
Ok(v)
}