blob: 54ea04a89c725705cae657e1dc765b79c9bd70dc [file] [log] [blame]
// Copyright 2023 Google LLC
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
///! Debug command traits and helpers for defining commands for integration
/// into a debug tool.
use std::str::FromStr;
/// A CommandSet is a set of commands (usually an enum) that each represent an
/// action that can be performed. i.e. 'list', 'volume' etc. Each command can
/// take zero or more arguments and have zero or more flags.
/// Typically an Enum of commands would implement CommandSet trait.
pub trait CommandSet: FromStr + ::core::fmt::Display {
/// Returns a vector of strings that are the commands supported by this.
fn variants() -> Vec<String>;
/// Returns a string listing the arguments that this command takes, in <>
/// brackets
fn arguments(&self) -> &'static str;
/// Returns a string displaying the flags that this command supports, in []
/// brackets
fn flags(&self) -> &'static str;
/// Returns a short description of this command
fn desc(&self) -> &'static str;
/// Help string for this variant (build from Display, arguments and flags by
/// default)
fn help_simple(&self) -> String {
format!("{self} {} {} -- {}", self.flags(), self.arguments(), self.desc())
}
/// Possibly multi-line help string for all variants of this set.
fn help_all() -> String {
Self::variants()
.into_iter()
.filter_map(|s| FromStr::from_str(&s).ok())
.map(|s: Self| format!("{}\n", s))
.collect()
}
}
/// Macro to help build CommandSets
#[macro_export]
macro_rules! gen_commandset {
($name:ident {
$($variant:ident = ($val:expr, [$($flag:expr),*], [$($arg:expr),*], $help:expr)),*,
}) => {
/// Enum of all possible commands
#[derive(PartialEq, Debug)]
pub enum $name {
$($variant),*
}
impl CommandSet for $name {
fn variants() -> Vec<String> {
let mut variants = Vec::new();
$(variants.push($val.to_string());)*
variants
}
fn arguments(&self) -> &'static str {
match self {
$(
$name::$variant => concat!($("<", $arg, "> ",)*)
),*
}
}
fn flags(&self) -> &'static str {
match self {
$(
$name::$variant => concat!($("[", $flag, "] ",)*)
),*
}
}
fn desc(&self) -> &'static str {
match self {
$(
$name::$variant => $help
),*
}
}
}
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match *self {
$($name::$variant => write!(f, $val)),* ,
}
}
}
impl ::std::str::FromStr for $name {
type Err = ();
fn from_str(s: &str) -> Result<$name, ()> {
match s {
$($val => Ok($name::$variant)),* ,
_ => Err(()),
}
}
}
}
}
/// CommandRunner is used to perform a specific task based on the
pub trait CommandRunner {
type Set: CommandSet;
fn run(
&self,
cmd: Self::Set,
args: Vec<String>,
) -> impl futures::Future<Output = Result<(), impl ::std::error::Error>>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gen_commandset_simple() {
gen_commandset! {
TestCmd {
One = ("one", [], [], "First Command"),
WithFlags = ("with-flags", ["-1","-2"], [], "Command with flags"),
WithArgs = ("with-args", [], ["arg", "two"], "Command with args"),
WithBoth = ("with-both", ["-w"], ["simple"], "Command with both flags and args"),
}
}
let cmd: TestCmd = "one".parse().unwrap();
assert_eq!(cmd, TestCmd::One);
let cmd2: TestCmd = "with-flags".parse().unwrap();
assert_eq!(cmd2.arguments(), "");
assert_eq!(cmd2.flags(), "[-1] [-2] ");
}
}