extern crate mkpasswd;
extern crate rand_core;
use mkpasswd::{alphabets, generate};
use rand_core::{OsRng, RngCore};
use std::borrow::Cow;
use std::env;
use std::io::{self, Error, ErrorKind, Write};
use std::process;
fn main() {
let (count, alphabet, length) = parse_args().unwrap_or_else(|message| {
eprintln!("error: {}", message);
process::exit(1)
});
if count == 1 {
generate(&alphabet, length)
.map_err(|e| {
Error::new(
ErrorKind::Other,
format!("failed to generate password: {}", e),
)
})
.and_then(|password| io::stdout().write_all(&password))
.unwrap_or_else(|e| {
eprintln!("error: {}", e);
process::exit(1)
});
} else {
let mut test = [0];
OsRng.try_fill_bytes(&mut test).unwrap_or_else(|e| {
eprintln!("error: failed to create random number generator: {}", e);
process::exit(1)
});
let _stdout = io::stdout();
let mut stdout = _stdout.lock();
for _ in 0..count {
match generate(&alphabet, length) {
Ok(password) => {
stdout.write_all(&password).unwrap();
stdout.write(b"\n").unwrap();
}
Err(e) => eprintln!("error: failed to get random data: {}", e),
}
}
}
}
fn parse_args() -> Result<(usize, Box<[u8]>, usize), Cow<'static, str>> {
let mut args = env::args_os().skip(1);
let mut count: usize = 0;
let mut length: usize = 0;
let mut alphabet: Option<Box<[u8]>> = None;
while let Some(arg) = args.next() {
if (arg == "-n" || arg == "--count") && count == 0 {
let arg = if let Some(arg) = args.next() {
arg
} else {
return Err(Cow::from(format!("expected value for {:?}", arg)));
};
match arg
.into_string()
.map_err(|_| Cow::from("invalid count"))
.and_then(|n| {
n.parse()
.map_err(|e| Cow::from(format!("invalid number: {}", e)))
}) {
Ok(n) => {
if n == 0 {
return Err(Cow::from("the count can not be 0"));
} else {
count = n;
}
}
Err(e) => return Err(Cow::from(e)),
};
} else if arg == "-h" || arg == "--help" {
process::exit(match io::stdout().write_all(include_bytes!("help.txt")) {
Ok(_) => 0,
Err(_) => 1,
});
} else {
let mut set_alphabet = |new_alphabet| {
if alphabet.is_some() {
eprintln!("warning: only one alphabet can be used");
} else {
alphabet = Some(new_alphabet);
};
};
if arg == "-a" || arg == "--alphabet" {
if let Some(arg) = args.next() {
if alphabet.is_some() {
eprintln!("warning: only one alphabet can be used");
args.next();
continue;
};
if arg.is_empty() {
return Err(Cow::from("a custom may alphabet not be empty"));
};
let mut custom_alphabet = match arg.into_string() {
Ok(string) => string.into_bytes(),
Err(_) => return Err(Cow::from("a custom alphabet must be valid UTF-8")),
};
custom_alphabet.sort();
custom_alphabet.dedup();
alphabet = Some(custom_alphabet.into_boxed_slice());
} else {
return Err(Cow::from(format!("expected value for {:?}", arg)));
};
} else if arg == "--password" {
set_alphabet(Box::new(alphabets::Password));
} else if arg == "--latin-numbers" {
set_alphabet(Box::new(alphabets::LatinNumbers));
} else if arg == "--base64" {
set_alphabet(Box::new(alphabets::Base64));
} else if arg == "--base64-url" {
set_alphabet(Box::new(alphabets::Base64Url));
} else if arg == "--numbers" {
set_alphabet(Box::new(alphabets::Numbers));
} else if arg == "--latin" {
set_alphabet(Box::new(alphabets::Latin));
} else if arg == "--latin-lower" {
set_alphabet(Box::new(alphabets::LatinLower));
} else if arg == "--latin-lower-numbers" {
set_alphabet(Box::new(alphabets::LatinLowerNumbers));
} else if arg == "--latin-upper" {
set_alphabet(Box::new(alphabets::LatinUpper));
} else if arg == "--latin-upper-numbers" {
set_alphabet(Box::new(alphabets::LatinUpperNumbers));
} else if length == 0 {
if let Ok(n) = arg
.into_string()
.map_err(|_| ())
.and_then(|n| n.parse().map_err(|_| ()))
{
if n == 0 {
return Err(Cow::from("password length can not be 0"));
};
length = n;
} else {
return Err(Cow::from("invalid password length"));
};
} else {
eprintln!("warning: unexpected {:?}", arg);
};
};
}
if count == 0 {
count = 1;
};
if length == 0 {
length = 32;
};
Ok((
count,
alphabet.unwrap_or_else(|| Box::new(alphabets::Password)),
length,
))
}