#![allow(dead_code, reason = "the demo just parses argv and prints the result")]
use std::time::Duration;
use pound::{
FromArg,
Parse,
ValueEnum,
ValueError,
};
#[derive(Debug)]
struct Ttl(Duration);
impl FromArg for Ttl {
fn from_arg(s: &str) -> Result<Self, ValueError> {
let split = s.find(|c: char| !c.is_ascii_digit()).unwrap_or(s.len());
let (num, unit) = s.split_at(split);
let n: u64 = num
.parse()
.map_err(|_| ValueError::new(s, "expected <number><s|m|h|d>"))?;
let secs = match unit {
"" | "s" => n,
"m" => n * 60,
"h" => n * 3_600,
"d" => n * 86_400,
other => {
return Err(ValueError::new(s, format!("unknown unit '{other}', use s/m/h/d")));
},
};
Ok(Self(Duration::from_secs(secs)))
}
}
#[derive(ValueEnum, Debug)]
enum Format {
Text,
Json,
Toml,
Env,
}
#[derive(ValueEnum, Debug)]
enum Kind {
Password,
Token,
Key,
Certificate,
}
#[derive(ValueEnum, Debug)]
enum OnConflict {
Skip,
Overwrite,
Fail,
}
#[derive(Parse, Debug)]
enum NsCmd {
Create {
name: String,
#[pound(long)]
desc: Option<String>,
},
#[pound(name = "ls")]
List {
#[pound(long)]
format: Option<Format>,
},
Rm {
name: String,
#[pound(short, long)]
force: bool,
},
Rename { from: String, to: String },
}
#[derive(Parse, Debug)]
enum Cmd {
Set {
key: String,
value: String,
#[pound(short = 'K', long)]
kind: Option<Kind>,
#[pound(short, long)]
tag: Vec<String>,
#[pound(long)]
ttl: Option<Ttl>,
#[pound(long)]
lock: bool,
},
Get {
key: String,
#[pound(short, long)]
format: Option<Format>,
#[pound(short, long, conflicts_with = "format")]
clip: bool,
},
List {
#[pound(short, long)]
tag: Option<String>,
#[pound(short, long)]
kind: Option<Kind>,
#[pound(short, long)]
format: Option<Format>,
#[pound(long)]
show: bool,
},
#[pound(alias = "delete")]
Rm {
key: String,
#[pound(short, long)]
force: bool,
},
Import {
#[pound(positional, value_name = "PATH")]
file: String,
#[pound(short, long)]
format: Option<Format>,
#[pound(long, default = "skip")]
on_conflict: OnConflict,
},
Export {
#[pound(short, long)]
format: Option<Format>,
#[pound(short, long, group = "dest")]
output: Option<String>,
#[pound(long, group = "dest")]
stdout: bool,
#[pound(short, long)]
tag: Option<String>,
#[pound(long)]
include_locked: bool,
},
Exec {
#[pound(trailing)]
command: Vec<String>,
},
Namespace {
#[pound(subcommand)]
cmd: NsCmd,
},
#[pound(name = "whoami")]
WhoAmI,
#[pound(hidden)]
Doctor,
}
#[derive(Parse, Debug)]
#[pound(name = "vault", version = "0.1.0", required_group = "auth")]
struct Cli {
#[pound(long, group = "auth")]
token: Option<String>,
#[pound(long, group = "auth")]
key_file: Option<String>,
#[pound(short, long, alias = "ns", default = "default")]
namespace: String,
#[pound(short = 'D', long = "database", env = "VAULT_DB", help = "path to the vault database file")]
db: Option<String>,
#[pound(short, long, count)]
verbose: u8,
#[pound(long)]
dry_run: bool,
#[pound(long, hidden)]
debug_internals: bool,
#[pound(subcommand)]
cmd: Cmd,
}
fn main() {
let cli = Cli::parse();
if cli.verbose > 1 {
eprintln!("{cli:?}");
}
}