ohcldiag_dev/
completions.rsuse crate::VmArg;
use crate::VmId;
use crate::new_client;
use clap::Parser;
use clap_dyn_complete::CustomCompleter;
use clap_dyn_complete::CustomCompleterFactory;
use pal_async::DefaultDriver;
use std::time::Duration;
#[derive(Parser)]
pub struct Completions {
shell: clap_dyn_complete::Shell,
}
impl Completions {
pub fn run(self) -> anyhow::Result<()> {
clap_dyn_complete::emit_completion_stub(
self.shell,
"ohcldiag-dev",
". complete",
&mut std::io::stdout(),
)?;
Ok(())
}
}
pub(crate) struct OhcldiagDevCompleteFactory {
pub driver: DefaultDriver,
}
impl CustomCompleterFactory for OhcldiagDevCompleteFactory {
type CustomCompleter = OhcldiagDevComplete;
async fn build(&self, ctx: &clap_dyn_complete::RootCtx<'_>) -> Self::CustomCompleter {
let vm = ctx.matches.try_get_one::<VmId>("VM").unwrap_or_default();
let client = if let Some(vm) = vm {
new_client(self.driver.clone(), &VmArg { id: vm.clone() }).ok()
} else {
None
};
OhcldiagDevComplete { client }
}
}
pub(crate) struct OhcldiagDevComplete {
client: Option<diag_client::DiagClient>,
}
impl CustomCompleter for OhcldiagDevComplete {
async fn complete(
&self,
ctx: &clap_dyn_complete::RootCtx<'_>,
subcommand_path: &[&str],
arg_id: &str,
) -> Vec<String> {
match (subcommand_path, arg_id) {
(["ohcldiag-dev"], "VM") => list_vms().unwrap_or_default(),
(["ohcldiag-dev", "inspect"], "path") => {
let on_error = vec!["failed/to/connect".into()];
let (parent_path, to_complete) = (ctx.to_complete)
.rsplit_once('/')
.unwrap_or(("", ctx.to_complete));
let Some(client) = self.client.as_ref() else {
return on_error;
};
let Ok(node) = client
.inspect(parent_path, Some(1), Some(Duration::from_secs(1)))
.await
else {
return on_error;
};
let mut completions = Vec::new();
if let inspect::Node::Dir(dir) = node {
for entry in dir {
if entry.name.starts_with(to_complete) {
if parent_path.is_empty() {
completions.push(format!("{}/", entry.name))
} else {
completions.push(format!(
"{}/{}{}",
parent_path,
entry.name,
if matches!(entry.node, inspect::Node::Dir(..)) {
"/"
} else {
""
}
))
}
}
}
}
completions
}
_ => Vec::new(),
}
}
}
#[cfg(windows)]
fn list_vms() -> anyhow::Result<Vec<String>> {
use anyhow::Context;
let output = std::process::Command::new("hvc.exe")
.arg("list")
.arg("-q")
.output()
.context("failed to invoke hvc.exe")?;
if output.status.success() {
let stdout = std::str::from_utf8(&output.stdout).context("stdout isn't utf8")?;
Ok(stdout.trim().lines().map(String::from).collect())
} else {
Ok(vec![".".into()])
}
}
#[cfg(not(windows))]
fn list_vms() -> anyhow::Result<Vec<String>> {
Ok(vec![".".into()])
}