| // 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] "); | |
| } | |
| } |