use std::path::PathBuf;
use crate::plugin::{PluginName, ENABLED_FILE};
pub const USAGE: &str = "\
Usage: wsb plugin <command>
list the plugins enabled on this host, and which are running
add <name> [--program <path>]
enable a plugin: it may register its services with the
daemon, and use the peer bridge. The program it is —
the <name> first on this PATH, or <path> — is recorded,
and a stamp hook that names the plugin runs that program
remove <name> disable it; it stops answering at once
A plugin's services are carried between paired workshops over the connection
they already hold, each switched per workshop and off until switched on:
`wsb peer service <peer> <plugin>.<service> on`.";
pub fn dispatch(args: &[String]) -> u8 {
let verb = args.get(2).map(String::as_str);
let rest = if args.len() > 3 { &args[3..] } else { &[] };
let result = match verb {
None | Some("list") => list(),
Some("add") => add(rest),
Some("remove") => remove(rest),
Some("help") | Some("--help") | Some("-h") => {
crate::outln!("{USAGE}");
return 0;
}
Some(other) => {
crate::errln!("wsb plugin: unknown command '{other}'\n\n{USAGE}");
return 2;
}
};
match result {
Ok(()) => 0,
Err(message) => {
crate::errln!("wsb plugin: {message}");
1
}
}
}
fn name(rest: &[String]) -> Result<PluginName, String> {
match rest {
[one] => PluginName::parse(one),
_ => Err(format!("name one plugin\n\n{USAGE}")),
}
}
fn add(rest: &[String]) -> Result<(), String> {
let (plugin, given) = match rest {
[one] => (PluginName::parse(one)?, None),
[one, flag, path] if flag == "--program" => (PluginName::parse(one)?, Some(PathBuf::from(path))),
_ => return Err(format!("name one plugin, and its program with --program <path> if you like\n\n{USAGE}")),
};
let host_dir = crate::daemon::host_dir()?;
let program = match given {
Some(path) if crate::platform::is_executable(&path) => Some(path),
Some(path) => return Err(format!("{} is not a program this account can run", path.display())),
None => crate::report::which(plugin.as_str()).map(PathBuf::from),
}
.map(|path| crate::platform::canonicalize(&path).map_err(|e| format!("cannot resolve {}: {e}", path.display())))
.transpose()?;
if crate::plugin::enable(&host_dir, &plugin)? {
crate::outln!("enabled plugin {plugin} on this host");
} else {
crate::outln!("plugin {plugin} was already enabled on this host");
}
match program {
Some(program) => {
crate::plugin::set_program(&host_dir, &plugin, &program)?;
crate::outln!(" its program is {}; a stamp hook that names {plugin} runs it", program.display());
}
None => crate::outln!(
" no {plugin} is on this PATH, so a stamp hook that names it is refused until it is \
named: wsb plugin add {plugin} --program <path>"
),
}
Ok(())
}
fn remove(rest: &[String]) -> Result<(), String> {
let plugin = name(rest)?;
let host_dir = crate::daemon::host_dir()?;
if crate::plugin::disable(&host_dir, &plugin)? {
crate::outln!("disabled plugin {plugin}: it answers nothing from now on");
} else {
crate::outln!(
"plugin {plugin} is not enabled on this host ({})",
host_dir.join(ENABLED_FILE).display()
);
}
Ok(())
}
fn list() -> Result<(), String> {
let host_dir = crate::daemon::host_dir()?;
let enabled = crate::plugin::enabled(&host_dir)?;
if enabled.is_empty() {
crate::outln!("no plugin is enabled on this host; `wsb plugin add <name>` enables one");
return Ok(());
}
let running: Vec<serde_json::Value> = match crate::daemon::WorkshopRendezvous::read(&host_dir)? {
None => Vec::new(),
Some(rendezvous) => {
let client = crate::daemon::Client::from_rendezvous(&rendezvous)?;
let answer: serde_json::Value = client.operator_get_json(crate::plugin::route::LIST)?;
answer["running"].as_array().cloned().unwrap_or_default()
}
};
for plugin in &enabled {
let entry = running.iter().find(|entry| entry["plugin"] == plugin.as_str());
match entry {
Some(entry) => {
let services: Vec<&str> = entry["services"]
.as_array()
.map(|services| services.iter().filter_map(|s| s.as_str()).collect())
.unwrap_or_default();
crate::outln!(
"{plugin} running (pid {}): {}",
entry["pid"],
services.join(", ")
);
}
None => crate::outln!("{plugin} enabled, not running"),
}
match crate::plugin::program(&host_dir, plugin.as_str())? {
Some(Some(program)) => crate::outln!(" program {}", program.display()),
_ => crate::outln!(" no program recorded: wsb plugin add {plugin} --program <path>"),
}
}
Ok(())
}