use guth_cli::{
discover_plugins, load_enabled_plugins, prune_uninstalled_enabled_plugins, run_plugin,
set_plugin_enabled, PluginManifest,
};
use std::ffi::OsString;
use std::process::ExitCode;
fn main() -> ExitCode {
match run(std::env::args_os().skip(1).collect()) {
Ok(code) => ExitCode::from(code),
Err(error) => {
eprintln!("guth-cli: {error}");
ExitCode::FAILURE
}
}
}
fn run(arguments: Vec<OsString>) -> Result<u8, String> {
let Some(command) = arguments.first().and_then(|value| value.to_str()) else {
print_help();
return Ok(0);
};
match command {
"plugins" => {
let plugins = discover_plugins().map_err(|error| error.to_string())?;
let enabled = load_enabled_plugins().map_err(|error| error.to_string())?;
print_plugins(&plugins, &enabled);
Ok(0)
}
"sync" => {
let plugins = discover_plugins().map_err(|error| error.to_string())?;
let enabled = load_enabled_plugins().map_err(|error| error.to_string())?;
dispatch_capability(&plugins, &enabled, "sync", &arguments[1..])
}
"run" => {
let plugins = discover_plugins().map_err(|error| error.to_string())?;
let enabled = load_enabled_plugins().map_err(|error| error.to_string())?;
let id = arguments
.get(1)
.and_then(|value| value.to_str())
.ok_or_else(|| {
"usage: guth-cli run <plugin-uuid> [-- plugin arguments]".to_string()
})?;
let plugin = plugins
.iter()
.find(|plugin| plugin.id.to_string() == id)
.ok_or_else(|| format!("plugin {id} is not installed"))?;
if !enabled.contains(&plugin.id) {
return Err(format!(
"plugin {id} is disabled; enable it in Guth or with `guth-cli enable {id}`"
));
}
dispatch(plugin, strip_separator(&arguments[2..]))
}
"enable" | "disable" => {
let id = arguments
.get(1)
.and_then(|value| value.to_str())
.ok_or_else(|| format!("usage: guth-cli {command} <plugin-uuid>"))?;
let id = uuid::Uuid::parse_str(id).map_err(|_| "invalid plugin UUID".to_string())?;
let plugins = discover_plugins().map_err(|error| error.to_string())?;
if command == "enable" && !plugins.iter().any(|plugin| plugin.id == id) {
return Err(format!("plugin {id} is not installed"));
}
set_plugin_enabled(id, command == "enable").map_err(|error| error.to_string())?;
println!("Plugin {id} {command}d.");
Ok(0)
}
"prune" => {
let enabled = prune_uninstalled_enabled_plugins().map_err(|error| error.to_string())?;
println!("Retained {} enabled plugins.", enabled.len());
Ok(0)
}
"help" | "--help" | "-h" => {
print_help();
Ok(0)
}
"version" | "--version" | "-V" => {
println!("guth-cli {}", env!("CARGO_PKG_VERSION"));
Ok(0)
}
_ => Err(format!("unknown command: {command}")),
}
}
fn dispatch_capability(
plugins: &[PluginManifest],
enabled: &std::collections::BTreeSet<uuid::Uuid>,
capability: &str,
arguments: &[OsString],
) -> Result<u8, String> {
let installed = plugins
.iter()
.filter(|plugin| plugin.supports(capability))
.collect::<Vec<_>>();
let matching = plugins
.iter()
.filter(|plugin| plugin.supports(capability) && enabled.contains(&plugin.id))
.collect::<Vec<_>>();
let plugin = match matching.as_slice() {
[] if !installed.is_empty() => {
return Err(format!(
"the installed {capability} plugin is disabled; enable it in Guth or with `guth-cli enable {}`",
installed[0].id
))
}
[] => return Err(format!("no {capability} plugin is installed")),
[plugin] => *plugin,
_ => {
return Err(format!(
"multiple {capability} plugins are installed; use `guth-cli run`"
))
}
};
dispatch(plugin, strip_separator(arguments))
}
fn dispatch(plugin: &PluginManifest, arguments: &[OsString]) -> Result<u8, String> {
let status =
run_plugin(plugin, arguments.iter().cloned()).map_err(|error| error.to_string())?;
Ok(status.code().unwrap_or(1).clamp(0, u8::MAX as i32) as u8)
}
fn strip_separator(arguments: &[OsString]) -> &[OsString] {
if arguments.first().is_some_and(|value| value == "--") {
&arguments[1..]
} else {
arguments
}
}
fn print_plugins(plugins: &[PluginManifest], enabled: &std::collections::BTreeSet<uuid::Uuid>) {
if plugins.is_empty() {
println!("No external Guth plugins installed.");
return;
}
for plugin in plugins {
println!(
"{} {} {} {} {}",
plugin.id,
plugin.name,
plugin.version,
if enabled.contains(&plugin.id) {
"enabled"
} else {
"disabled"
},
plugin.capabilities.join(",")
);
}
}
fn print_help() {
println!(
"Guth CLI {}\n\nUSAGE:\n guth-cli plugins\n guth-cli enable <plugin-uuid>\n guth-cli disable <plugin-uuid>\n guth-cli prune\n guth-cli sync -- <arguments>\n guth-cli run <plugin-uuid> -- <arguments>\n\nExternal plugin arguments and output are passed through without being persisted.",
env!("CARGO_PKG_VERSION")
);
}