mod certificate;
mod decrypt;
mod encrypt;
mod inject;
mod key;
mod models;
mod read;
mod run;
mod secret;
use akv_cli::{
parsing::{parse_date_time_opt, Resource},
ErrorKind, Result,
};
use clap::{ArgAction, Args, CommandFactory, Subcommand, ValueEnum};
use clap_complete::{generate, Shell};
use std::{borrow::Cow, collections::HashMap, io};
use time::OffsetDateTime;
use url::Url;
const VAULT_ENV_NAME: &str = "AZURE_KEYVAULT_URL";
#[derive(Debug, Subcommand)]
pub enum Commands {
Inject(inject::Args),
Run(run::Args),
Read(read::Args),
Encrypt(encrypt::Args),
Decrypt(decrypt::Args),
#[command(subcommand)]
Secret(secret::Commands),
#[command(subcommand)]
Key(key::Commands),
#[command(subcommand)]
Certificate(certificate::Commands),
Completion {
#[arg(value_enum)]
shell: Shell,
},
}
impl Commands {
pub async fn handle(&self, global_args: &crate::Args) -> Result<()> {
match self {
Commands::Secret(command) => command.handle(global_args).await,
Commands::Key(command) => command.handle(global_args).await,
Commands::Certificate(command) => command.handle(global_args).await,
Commands::Inject(args) => args.inject().await,
Commands::Read(args) => args.read().await,
Commands::Run(args) => args.run().await,
Commands::Encrypt(args) => args.encrypt().await,
Commands::Decrypt(args) => args.decrypt().await,
Commands::Completion { shell } => {
let mut cmd = super::Args::command();
let bin_name = cmd.get_name().to_string();
generate(*shell, &mut cmd, bin_name, &mut io::stdout());
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Args)]
pub struct AttributeArgs {
#[arg(long = "disabled", action = ArgAction::SetFalse, default_value_t = true)]
pub enabled: bool,
#[arg(long, value_parser = parse_date_time_opt)]
pub expires: Option<OffsetDateTime>,
#[arg(long, value_parser = parse_date_time_opt)]
pub not_before: Option<OffsetDateTime>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
pub enum OutputFormat {
#[default]
Default,
Json,
}
trait IsDefault {
fn is_default(&self) -> bool;
fn default_or(self) -> Option<Self>
where
Self: Sized,
{
if self.is_default() {
return None;
}
Some(self)
}
}
fn elapsed(
formatter: &timeago::Formatter,
now: OffsetDateTime,
d: Option<time::OffsetDateTime>,
) -> String {
d.map(|time| now - time)
.and_then(|time| time.try_into().ok())
.map_or_else(String::new, |d| formatter.convert(d))
}
fn map_tags(tags: &[(String, Option<String>)]) -> Option<HashMap<String, String>> {
if tags.is_empty() {
None
} else {
Some(HashMap::from_iter(tags.iter().map(|(k, v)| {
(k.to_string(), v.clone().unwrap_or_default())
})))
}
}
fn map_vec<'a, T, U, F>(v: Option<&'a [T]>, f: F) -> Option<Vec<U>>
where
U: From<&'a T>,
F: FnMut(&'a T) -> U,
{
match v {
Some([]) => None,
Some(v) => Some(v.iter().map(f).collect()),
None => None,
}
}
#[allow(clippy::type_complexity)]
fn select<'a>(
id: Option<&Url>,
vault: Option<&'a Url>,
name: Option<&'a String>,
version: Option<&'a String>,
) -> akv_cli::Result<(Cow<'a, str>, Cow<'a, str>, Option<Cow<'a, str>>)> {
match (id, vault, name) {
(Some(id), _, None) => {
let resource: Resource = id.try_into()?;
Ok((
Cow::Owned(resource.vault_url),
Cow::Owned(resource.name),
resource.version.map(Cow::Owned),
))
}
(None, Some(vault), Some(name)) => Ok((
Cow::Borrowed(vault.as_str()),
Cow::Borrowed(name),
version.map(|v| Cow::Borrowed(v.as_str())),
)),
_ => Err(akv_cli::Error::with_message(
ErrorKind::InvalidData,
"invalid arguments",
)),
}
}