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 126 127 128 129 130 131 132 133 134
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]
#[cfg_attr(nightly, doc(cfg(feature = "async")))]
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>
) -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = Result<(), Box<dyn ::std::error::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: &mut (), args: Vec<String>) -> Result<(), Box<dyn Error>> {
/// //--snip--
/// # Ok(())
/// }
///
/// fn main() {
/// // Creates a shell
/// let mut shell = Shell::new((), "[Shell]-$");
///
/// // Creates a command
/// shell.commands.insert(
/// "greet",
/// 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;
///
/// async fn greet(_state: &mut (), args: Vec<String>) -> Result<(), Box<dyn Error>> {
/// //--snip--
/// # Ok(())
/// }
///
/// async fn async_main() {
/// // Creates a shell
/// let mut shell = Shell::new((), "[Shell]-$");
///
/// // Creates a command
/// shell.commands.insert(
/// "greet",
/// Command::new_async("greets_you".to_string(), async_fn!((), greet)),
/// );
/// }
/// ```
#[cfg(feature = "async")]
pub 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>),
}