use std::path::PathBuf;
use crate::cli::completer;
use crate::cli::parser;
use crate::models::password_store::{OneTimePasswordAlgorithm, OneTimePasswordType};
use clap::ValueHint::{DirPath, FilePath};
use clap::{Args, Parser, Subcommand};
use clap_verbosity_flag::InfoLevel;
use serde::{Deserialize, Serialize};
#[derive(Parser)]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
#[command(flatten)]
pub verbose: clap_verbosity_flag::Verbosity<InfoLevel>,
#[arg(short = 'O', long)]
pub offline: bool,
}
#[derive(Subcommand)]
pub enum Commands {
Config {
#[command(subcommand)]
command: ConfigCommands,
},
Export {
#[command(subcommand)]
command: ExportCommands,
},
Hook {
#[command(subcommand)]
command: HookCommands,
},
Identity {
#[command(subcommand)]
command: IdentityCommands,
},
Otp {
#[command(subcommand)]
command: OtpCommands,
},
Recipient {
#[command(subcommand)]
command: RecipientCommands,
},
Secret {
#[command(subcommand)]
command: SecretCommands,
},
Store {
#[command(subcommand)]
command: StoreCommands,
},
}
#[derive(Subcommand)]
pub enum ConfigCommands {
Get(ConfigGetArgs),
Set(ConfigSetArgs),
}
#[derive(Args)]
pub struct ConfigGetArgs {
pub option: ConfigurationOption,
}
#[derive(Args)]
pub struct ConfigSetArgs {
pub option: ConfigurationOption,
pub value: String,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, clap::ValueEnum)]
pub enum ConfigurationOption {
IgnoreMissingIdentities,
ClipboardTimeout,
PullIntervalSeconds,
PushIntervalSeconds,
}
#[derive(Subcommand)]
pub enum ExportCommands {
Bitwarden(BitwardenArgs),
}
#[derive(Args)]
pub struct BitwardenArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(long)]
pub organization_id: Option<String>,
#[arg(long, requires = "organization_id")]
pub collection_id: Option<String>,
#[arg(long, requires = "organization_id")]
pub collection_name: Option<String>,
#[arg(long, default_values_t = [String::from("login"), String::from("email"), String::from("username")])]
pub username_keys: Vec<String>,
#[arg(long, default_values_t = [String::from("uri"), String::from("url"), String::from("link"), String::from("site")])]
pub uri_keys: Vec<String>,
#[arg(long, default_missing_value="true", default_value("false"), num_args=0..=1)]
pub pretty: Option<bool>,
}
#[derive(Subcommand)]
pub enum HookCommands {
Set(HookSetArgs),
Get(HookGetArgs),
Run(HookRunArgs),
}
#[derive(Args)]
pub struct HookSetArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long, conflicts_with = "store")]
pub global: bool,
#[arg(long)]
pub pull: Vec<String>,
#[arg(long)]
pub push: Vec<String>,
#[arg(long, conflicts_with = "append")]
pub prepend: bool,
#[arg(long, conflicts_with = "prepend")]
pub append: bool,
}
#[derive(Args)]
pub struct HookGetArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long, conflicts_with = "store")]
pub global: bool,
}
#[derive(Args)]
pub struct HookRunArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(
long,
default_missing_value = "true",
default_value("false"),
num_args=0..=1,
conflicts_with = "store"
)]
pub all: Option<bool>,
#[arg(long, default_missing_value="true", default_value("false"), num_args=0..=1)]
pub pull: Option<bool>,
#[arg(long, default_missing_value="true", default_value("false"), num_args=0..=1)]
pub push: Option<bool>,
}
#[derive(Subcommand)]
pub enum OtpCommands {
Add(OtpAddArgs),
Remove(OtpRemoveArgs),
List(OtpListArgs),
Show(OtpShowArgs),
Copy(OtpCopyArgs),
Move(OtpMoveArgs),
}
#[derive(Args)]
pub struct OtpAddArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
#[arg(long, conflicts_with_all = ["qrcode"])]
pub url: Option<String>,
#[arg(long, value_hint = FilePath, value_parser = parser::existing_file, conflicts_with_all = ["url"])]
pub qrcode: Option<PathBuf>,
#[arg(long, conflicts_with_all = ["url", "qrcode"])]
pub secret: Option<String>,
#[arg(long = "type", conflicts_with_all = ["url", "qrcode"])]
pub otp_type: Option<OneTimePasswordType>,
#[arg(long, conflicts_with_all = ["url", "qrcode"])]
pub algorithm: Option<OneTimePasswordAlgorithm>,
#[arg(long, conflicts_with_all = ["url", "qrcode"])]
pub digits: Option<u8>,
#[arg(long, conflicts_with_all = ["url", "qrcode", "counter"])]
pub period: Option<u64>,
#[arg(long, group = "manual", conflicts_with_all = ["url", "qrcode", "counter"])]
pub skew: Option<u64>,
#[arg(long, group = "manual", conflicts_with_all = ["url", "qrcode", "period"])]
pub counter: Option<u64>,
pub password_path: String,
}
#[derive(Args)]
pub struct OtpRemoveArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
pub password_path: String,
}
#[derive(Args)]
pub struct OtpListArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub tree: bool,
}
#[derive(Args)]
pub struct OtpShowArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub clip: bool,
pub password_path: String,
}
#[derive(Args)]
pub struct OtpCopyArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
pub source_path: String,
pub target_path: String,
}
#[derive(Args)]
pub struct OtpMoveArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
pub current_path: String,
pub new_path: String,
}
#[derive(Subcommand)]
pub enum IdentityCommands {
Add(IdentityAddArgs),
Remove(IdentityRemoveArgs),
List(IdentityListArgs),
}
#[derive(Args)]
pub struct IdentityAddArgs {
#[arg(short, long, value_hint = FilePath, value_parser = parser::existing_file)]
pub file: PathBuf,
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long, conflicts_with = "store")]
pub global: bool,
}
#[derive(Args)]
pub struct IdentityRemoveArgs {
#[arg(short, long, value_hint = FilePath)]
pub file: PathBuf,
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long, conflicts_with = "store")]
pub global: bool,
#[arg(short, long)]
pub ignore_unknown: bool,
}
#[derive(Args)]
pub struct IdentityListArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub global: bool,
}
#[derive(Subcommand)]
pub enum RecipientCommands {
Add(RecipientAddArgs),
Remove(RecipientRemoveArgs),
List(RecipientListArgs),
}
#[derive(Args)]
pub struct RecipientAddArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[command(flatten)]
pub keys: RecipientKeysArgs,
#[arg(short, long)]
pub name: Option<String>,
}
#[derive(Args)]
#[group(required = true, multiple = false)]
pub struct RecipientKeysArgs {
#[arg(short, long)]
pub public_key: Option<String>,
#[arg(short, long)]
pub file: Option<String>,
#[arg(long)]
pub codeberg: Option<String>,
#[arg(long)]
pub github: Option<String>,
#[arg(long)]
pub gitlab: Option<String>,
}
#[derive(Args)]
pub struct RecipientRemoveArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub ignore_unknown: bool,
pub public_key: String,
}
#[derive(Args)]
pub struct RecipientListArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
}
#[derive(Subcommand)]
pub enum SecretCommands {
Add(SecretAddArgs),
Audit(SecretAuditArgs),
Copy(SecretCopyArgs),
Edit(SecretEditArgs),
Generate(SecretGenerateArgs),
Grep(SecretGrepArgs),
List(SecretListArgs),
Move(SecretMoveArgs),
Remove(SecretRemoveArgs),
Show(SecretShowArgs),
}
#[derive(Args)]
pub struct SecretAddArgs {
#[arg(short, long)]
pub multiline: bool,
#[arg(short, long)]
pub force: bool,
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
pub secret_path: String,
}
#[derive(Args)]
pub struct SecretAuditArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
pub secret_path: Option<String>,
}
#[derive(Args)]
pub struct SecretCopyArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
pub source_path: String,
pub target_path: String,
}
#[derive(Args)]
pub struct SecretEditArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
pub secret_path: String,
}
#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct SecretGenerateArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
#[arg(short, long)]
pub inplace: bool,
pub secret_path: String,
#[arg(short, long, default_value_t = 25)]
pub length: usize,
#[arg(short, long, default_value_t = true)]
pub numbers: bool,
#[arg(short = 'j', long, default_value_t = true)]
pub lowercase_letters: bool,
#[arg(short, long, default_value_t = true)]
pub uppercase_letters: bool,
#[arg(short = 'y', long, default_value_t = false)]
pub symbols: bool,
#[arg(short = 'w', long, default_value_t = false)]
pub spaces: bool,
#[arg(short, long, default_value_t = true)]
pub exclude_similar_characters: bool,
#[arg(short = 't', long, default_value_t = true)]
pub strict: bool,
}
#[derive(Args)]
pub struct SecretGrepArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub regex: bool,
pub search_string: String,
}
#[derive(Args)]
pub struct SecretListArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub tree: bool,
}
#[derive(Args)]
pub struct SecretMoveArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
pub current_path: String,
pub new_path: String,
}
#[derive(Args)]
pub struct SecretRemoveArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short, long)]
pub force: bool,
pub secret_path: String,
}
#[derive(Args)]
pub struct SecretShowArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(short = 'o', long, conflicts_with = "clip")]
pub qrcode: bool,
#[arg(short, long, conflicts_with = "qrcode")]
pub clip: bool,
#[arg(short, long)]
pub line: Option<isize>,
pub secret_path: String,
}
#[derive(Subcommand)]
pub enum StoreCommands {
Add(StoreAddArgs),
Decrypt(StoreDecryptArgs),
Exec(StoreExecArgs),
List(StoreListArgs),
Merge(StoreMergeArgs),
Remove(StoreRemoveArgs),
SetDefault(StoreSetDefaultArgs),
}
#[derive(Args)]
pub struct StoreAddArgs {
#[arg(short, long, value_hint = DirPath)]
pub path: PathBuf,
#[arg(short, long)]
pub name: String,
#[arg(short, long)]
pub default: bool,
}
#[derive(Args)]
pub struct StoreRemoveArgs {
#[arg(add = completer::store_name(), value_parser = parser::store_name)]
pub store: Option<String>,
#[arg(short, long)]
pub remove_data: bool,
}
#[derive(Args)]
pub struct StoreSetDefaultArgs {
#[arg(value_parser = parser::store_name)]
pub name: String,
}
#[derive(Args)]
pub struct StoreExecArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(num_args=0..)]
pub command: Vec<String>,
}
#[derive(Args)]
pub struct StoreDecryptArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(long, value_hint = FilePath)]
pub store_path: Option<PathBuf>,
}
#[derive(Args)]
pub struct StoreMergeArgs {
#[command(flatten)]
pub store_selection: StoreSelectionArgs,
#[arg(long, value_hint = FilePath)]
pub common_ancestor: PathBuf,
#[arg(long, value_hint = FilePath)]
pub current_version: PathBuf,
#[arg(long, value_hint = FilePath)]
pub other_version: PathBuf,
}
#[derive(Args)]
pub struct StoreListArgs {}
#[derive(Args)]
pub struct StoreSelectionArgs {
#[arg(short, long, add = completer::store_name(), value_parser = parser::store_name)]
pub store: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verify_cli() {
use clap::CommandFactory;
Cli::command().debug_assert();
}
}