#![allow(missing_docs)]
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::ffi::OsString;
#[derive(Debug, Deserialize, Serialize)]
#[serde(default)]
struct Config {
log_level: librebar::config::LogLevel,
}
impl Default for Config {
fn default() -> Self {
Self {
log_level: librebar::config::LogLevel::Info,
}
}
}
#[derive(Parser)]
#[command(
name = "plugin-cli",
about = "Main CLI that dispatches unknown subcommands to `plugin-cli-*` binaries on PATH"
)]
struct Cli {
#[command(flatten)]
common: librebar::cli::CommonArgs,
#[command(subcommand)]
command: Option<Command>,
}
#[derive(Subcommand)]
enum Command {
Info,
Which {
name: String,
},
#[command(external_subcommand)]
External(Vec<OsString>),
}
fn main() -> Result<()> {
let cli = Cli::parse();
cli.common.apply_color();
cli.common.apply_chdir()?;
let app = librebar::init("plugin-cli")
.with_version(env!("CARGO_PKG_VERSION"))
.with_cli(cli.common)
.config::<Config>()
.logging()
.start()?;
match cli.command.unwrap_or(Command::Info) {
Command::Info => print_info(&app),
Command::Which { name } => print_which(&app, &name),
Command::External(args) => dispatch_external(&app, args),
}
}
fn print_info(app: &librebar::App<Config>) -> Result<()> {
println!("app: {} v{}", app.app_name(), app.version());
println!("sources: {:?}", app.config_sources());
println!(
"log dir: {:?}",
librebar::logging::platform_log_dir(app.app_name())
);
println!("built-ins: info, which");
println!("plugins: any `plugin-cli-<name>` binary on PATH (try `which hello-greet`)");
Ok(())
}
fn print_which(app: &librebar::App<Config>, name: &str) -> Result<()> {
let binary = librebar::dispatch::subcommand_binary(app.app_name(), name);
match librebar::dispatch::resolve(app.app_name(), name) {
Some(path) => {
println!("{binary} -> {}", path.display());
Ok(())
}
None => {
anyhow::bail!("{binary} not found on PATH");
}
}
}
fn dispatch_external(app: &librebar::App<Config>, args: Vec<OsString>) -> Result<()> {
let (sub, rest) = args
.split_first()
.context("external subcommand requires a name")?;
let sub_str = sub.to_string_lossy();
tracing::info!(subcommand = %sub_str, "dispatching to external plugin");
match librebar::dispatch::run(app.app_name(), &sub_str, rest)? {
Some(status) => {
let code = status.code().unwrap_or(1);
if code != 0 {
std::process::exit(code);
}
Ok(())
}
None => {
let expected = librebar::dispatch::subcommand_binary(app.app_name(), &sub_str);
anyhow::bail!(
"unknown subcommand: {sub_str}. Install `{expected}` on PATH to provide it."
);
}
}
}