use clap::{Args, Parser, ValueEnum};
use passgen::random::Random;
use std::{path::PathBuf, str::FromStr};
#[derive(Clone)]
pub struct WordlistSpec {
pub name: String,
pub path: PathBuf,
}
#[derive(thiserror::Error, Debug)]
pub enum WordlistSpecParseError {
#[error("missing colon")]
MissingColon,
}
impl FromStr for WordlistSpec {
type Err = WordlistSpecParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let (name, path) = input
.split_once(':')
.ok_or(WordlistSpecParseError::MissingColon)?;
Ok(Self {
name: name.into(),
path: path.into(),
})
}
}
#[derive(ValueEnum, Clone)]
pub enum Format {
Text,
Json,
Csv,
}
#[derive(Parser)]
#[command(author, version)]
pub struct Options {
#[clap(short, long)]
pub preset: Option<String>,
#[clap(short, long, default_value = "1")]
pub amount: usize,
#[clap(short, long)]
pub random: Option<Random>,
#[clap(flatten)]
pub master: MasterOptions,
#[clap(short, long, default_value = "text")]
pub format: Format,
#[clap(short = 'j', long)]
pub threads: Option<usize>,
#[clap(short, long)]
pub verbose: Option<usize>,
#[clap(short, long)]
pub wordlist: Vec<WordlistSpec>,
#[clap(required_unless_present("preset"))]
pub pattern: Option<String>,
pub config: Vec<PathBuf>,
}
#[derive(Args)]
#[group()]
pub struct MasterOptions {
#[clap(short, long, conflicts_with("random"))]
pub master: Option<String>,
#[clap(short, long, requires("master"))]
pub domain: Option<String>,
#[clap(short, long, requires("master"))]
pub token: Option<String>,
}
impl Options {
pub fn random(&self) -> Random {
self.master
.random()
.or(self.random.clone())
.unwrap_or_default()
}
}
impl MasterOptions {
pub fn random(&self) -> Option<Random> {
self.master
.as_deref()
.map(|master| Random::from_master_pass(master, self.domain.as_deref().unwrap_or("")))
}
}