use crate::error::{OlError, OL_4270_CONFIG_UNREADABLE};
use clap::{Parser, Subcommand, ValueEnum};
pub mod commands;
#[derive(Parser, Debug)]
#[command(
name = "openlatch-provider",
version,
about = "Publish and run security tools on OpenLatch",
long_about = None,
disable_help_subcommand = true,
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Command>,
#[arg(long, global = true, value_name = "NAME")]
pub profile: Option<String>,
#[arg(long, value_enum, global = true, value_name = "FORMAT")]
pub output: Option<OutputFormat>,
#[arg(long, short = 'q', global = true)]
pub quiet: bool,
#[arg(long, short = 'v', global = true)]
pub verbose: bool,
#[arg(long, global = true)]
pub debug: bool,
#[arg(long, global = true)]
pub no_color: bool,
#[arg(long, short = 'y', global = true)]
pub yes: bool,
#[arg(long, global = true)]
pub dry_run: bool,
#[arg(long, global = true)]
pub non_interactive: bool,
#[arg(long, hide = true)]
pub markdown_help: bool,
}
#[derive(Debug, Clone, Default)]
pub struct GlobalArgs {
pub profile: Option<String>,
pub output: Option<OutputFormat>,
pub quiet: bool,
pub verbose: bool,
pub debug: bool,
pub no_color: bool,
pub yes: bool,
pub dry_run: bool,
pub non_interactive: bool,
}
impl GlobalArgs {
fn from_cli(cli: &Cli) -> Self {
Self {
profile: cli.profile.clone(),
output: cli.output,
quiet: cli.quiet,
verbose: cli.verbose || cli.debug,
debug: cli.debug,
no_color: cli.no_color,
yes: cli.yes,
dry_run: cli.dry_run,
non_interactive: cli.non_interactive,
}
}
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
Table,
Json,
Yaml,
Sarif,
}
#[derive(Subcommand, Debug)]
pub enum Command {
Login(commands::auth::LoginArgs),
Logout,
Whoami,
Init(commands::init::InitArgs),
New {
#[command(subcommand)]
kind: NewKind,
},
Editor {
#[command(subcommand)]
action: EditorAction,
},
Register(commands::register::RegisterArgs),
Publish(commands::publish::PublishArgs),
Deprecate(commands::tools::DeprecateArgs),
Tools {
#[command(subcommand)]
action: ToolsAction,
},
Providers {
#[command(subcommand)]
action: ProvidersAction,
},
Bindings {
#[command(subcommand)]
action: BindingsAction,
},
Listen(commands::listen::ListenArgs),
Trigger(commands::trigger::TriggerArgs),
Tail(commands::tail::TailArgs),
Doctor,
#[command(name = "self")]
SelfCmd {
#[command(subcommand)]
action: SelfAction,
},
Config {
#[command(subcommand)]
action: ConfigAction,
},
}
#[derive(Subcommand, Debug)]
pub enum NewKind {
Tool {
#[arg(long, value_enum)]
template: ToolTemplate,
#[arg(long)]
out: Option<String>,
},
}
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolTemplate {
Python,
Rust,
Node,
}
#[derive(Subcommand, Debug)]
pub enum EditorAction {
Update(commands::editor::UpdateArgs),
}
#[derive(Subcommand, Debug)]
pub enum ToolsAction {
List,
Delete {
slug: String,
#[arg(long)]
yes: bool,
},
Deprecate(commands::tools::DeprecateArgs),
}
#[derive(Subcommand, Debug)]
pub enum ProvidersAction {
List,
Update { slug: String },
Delete {
slug: String,
#[arg(long)]
yes: bool,
},
}
#[derive(Subcommand, Debug)]
pub enum BindingsAction {
List,
RotateSecret {
id: String,
#[arg(long)]
yes: bool,
},
DeleteSecret {
id: String,
#[arg(long)]
yes: bool,
},
Probe { id: String },
Metrics { id: String },
Delete {
id: String,
#[arg(long)]
yes: bool,
},
}
#[derive(Subcommand, Debug)]
pub enum SelfAction {
Update {
#[arg(long)]
check: bool,
#[arg(long)]
apply: bool,
#[arg(long)]
channel: Option<String>,
},
}
#[derive(Subcommand, Debug)]
pub enum ConfigAction {
Get { key: String },
Set { key: String, value: String },
List,
}
pub async fn dispatch() -> Result<(), OlError> {
let cli = Cli::parse();
if cli.markdown_help {
eprintln!("--markdown-help: clap-markdown integration is wired in P3.T6");
return Ok(());
}
let g = GlobalArgs::from_cli(&cli);
match cli.command {
None => {
let out = crate::ui::output::OutputConfig::resolve(&g);
crate::ui::header::print_full_banner(&out);
<Cli as clap::CommandFactory>::command()
.print_help()
.map_err(|e| {
OlError::new(OL_4270_CONFIG_UNREADABLE, format!("printing help: {e}"))
})?;
println!();
Ok(())
}
Some(Command::Login(args)) => commands::auth::login(&g, args).await,
Some(Command::Logout) => commands::auth::logout(&g).await,
Some(Command::Whoami) => commands::auth::whoami(&g).await,
Some(Command::Init(args)) => commands::init::run(&g, args).await,
Some(Command::New { kind }) => commands::new::run(&g, kind).await,
Some(Command::Editor { action }) => commands::editor::run(&g, action).await,
Some(Command::Register(args)) => commands::register::run(&g, args).await,
Some(Command::Publish(args)) => commands::publish::run(&g, args).await,
Some(Command::Deprecate(args)) => commands::tools::deprecate(&g, args).await,
Some(Command::Tools { action }) => commands::tools::run(&g, action).await,
Some(Command::Providers { action }) => commands::providers::run(&g, action).await,
Some(Command::Bindings { action }) => commands::bindings::run(&g, action).await,
Some(Command::Listen(args)) => commands::listen::run(&g, args).await,
Some(Command::Trigger(args)) => commands::trigger::run(&g, args).await,
Some(Command::Tail(args)) => commands::tail::run(&g, args).await,
Some(Command::Doctor) => commands::doctor::run(&g).await,
Some(Command::SelfCmd { action }) => commands::self_update::run(&g, action).await,
Some(Command::Config { action }) => commands::config::run(&g, action).await,
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn cli_definition_compiles() {
Cli::command().debug_assert();
}
}