1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
use std::error::Error; #[cfg(feature = "async")] use std::{ future::Future, pin::Pin }; // NOTE: Taken from StackOverflow /// Use this macro to wrap an asynchronous function so that it can be used /// as a function pointer. /// /// This is used with async [`Command`s](Command). /// /// The first argument is a Type, which is the state. The second is the /// async function. #[macro_export] macro_rules! async_fn { ($state:ty, $inc:expr) => {{ // I think the error message referred to here is spurious, but why take a chance? fn rustc_complains_if_this_name_conflicts_with_the_environment_even_though_its_probably_fine(state: &mut $state, args: Vec<String>) -> Pin<Box<dyn Future<Output = Result<(), Box< dyn Error>>> + Send + '_ >> { Box::pin($inc(state, args)) } rustc_complains_if_this_name_conflicts_with_the_environment_even_though_its_probably_fine }} } #[derive(Clone)] pub struct Command<T> { /// The function pointer which this links to. pub command: CommandType<T>, /// A help string, should be less than 80 characters. For example, if it /// was an `echo` command: /// ```txt /// prints the arguments to the output. /// ``` pub help: String, } impl<T> Command<T> { /// Creates a new `Command`. /// /// # Example /// /// ```rust /// use shellfish::*; /// use std::error::Error; /// /// fn greet(_state: (), args: Vec<String>) -> Result<(), Box<dyn Error> { /// //--snip-- /// } /// /// fn main() { /// // Creates a shell /// let shell = Shell::new((), "[Shell]-$"); /// /// // Creates a command /// shell.commands.insert( /// "greet".to_string(), /// Command::new("greets_you".to_string(), greet); /// ); /// } /// ``` pub fn new(help: String, command: CommandFn<T>) -> Self { Self { command: CommandType::Sync(command), help } } /// Creates a new asynchronous `Command`. /// /// It is important to note that you have to call /// [`async_fn!`](async_fn!) to prepare a function for it. /// /// # Example /// /// ```rust /// use shellfish::*; /// use std::error::Error; /// /// #[macro_use] /// extern crate shellfish; /// /// async fn greet(_state: (), args: Vec<String>) -> Result<(), Box<dyn Error> { /// //--snip-- /// } /// /// #[tokio::main] /// fn main() { /// // Creates a shell /// let shell = Shell::new((), "[Shell]-$"); /// /// // Creates a command /// shell.commands.insert( /// "greet".to_string(), /// Command::new_async("greets_you".to_string(), async_fn!((), greet)); /// ); /// } /// ``` #[cfg(feature = "async")] pub async fn new_async(help: String, command: AsyncCommandFn<T>) -> Self { Self { command: CommandType::Async(command), help } } } /// Stores a function for a [`Command`](Command). /// /// It requires the function returns a `Result<(), Box<dyn Error>>`. pub type CommandFn<T> = fn(&mut T, Vec<String>) -> Result<(), Box<dyn Error>>; /// Stores an asynchronous function for a [`Command`](Command). /// /// It requires the function returns a `Result<(), Box<dyn Error>>`. /// /// To prepare for this you have to use the [`async_fn!`](async_fn!) /// macro to prepare the function. #[cfg(feature = "async")] pub type AsyncCommandFn<T> = fn(&mut T, Vec<String>) -> Pin<Box<dyn Future<Output = Result<(), Box< dyn Error>>> + Send + '_ >>; /// Command type specifies what type of command this is, namely wether it /// is async or not. #[derive(Clone)] pub enum CommandType<T> { Sync(CommandFn<T>), #[cfg(feature = "async")] Async(AsyncCommandFn<T>), }