Skip to main content

stoat/commands/
command.rs

1use std::{
2    collections::HashMap,
3    fmt::{self, Debug},
4    marker::PhantomData,
5    sync::Arc,
6};
7
8use async_fn_traits::{AsyncFn1, AsyncFn2, AsyncFn3, AsyncFn4};
9use async_trait::async_trait;
10
11use crate::{
12    Error,
13    commands::{Context, Converter, checks::Check},
14};
15
16#[derive(Clone)]
17pub struct Command<
18    E: From<Error> + Clone + Debug + Send + Sync + 'static,
19    S: Debug + Clone + Send + Sync + 'static,
20> {
21    pub name: String,
22    pub handle: Arc<dyn CommandHandle<(), E, S>>,
23    pub error: Option<Arc<dyn CommandErrorHandler<E, S>>>,
24    pub children: HashMap<String, Command<E, S>>,
25    pub checks: Vec<Arc<dyn Check<E, S>>>,
26    pub aliases: Vec<String>,
27    pub description: Option<String>,
28    pub signature: Option<String>,
29    pub parents: Vec<String>,
30    pub hidden: bool,
31}
32
33impl<
34    E: From<Error> + Clone + Debug + Send + Sync + 'static,
35    S: Debug + Clone + Send + Sync + 'static,
36> fmt::Debug for Command<E, S> {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        f.debug_struct("Command")
39            .field("name", &self.name)
40            .field("description", &self.description)
41            .field("children", &self.children)
42            .field("aliases", &self.aliases)
43            .field("signature", &self.signature)
44            .finish_non_exhaustive()
45    }
46}
47
48impl<
49    E: From<Error> + Clone + Debug + Send + Sync + 'static,
50    S: Debug + Clone + Send + Sync + 'static,
51> Command<E, S>
52{
53    pub fn new<T: Send + Sync + 'static, I: Into<String>, F: CommandHandle<T, E, S> + Clone>(
54        name: I,
55        handle: F,
56    ) -> Self {
57        let erased = ErasedCommandHandler {
58            handle,
59            _p: PhantomData,
60        };
61
62        Self {
63            name: name.into(),
64            handle: Arc::new(erased),
65            error: None,
66            children: HashMap::new(),
67            checks: Vec::new(),
68            aliases: Vec::new(),
69            description: None,
70            signature: None,
71            parents: Vec::new(),
72            hidden: false,
73        }
74    }
75
76    pub fn child(mut self, mut command: Self) -> Self {
77        command.parents = self.parents.clone();
78        command.parents.push(self.name.clone());
79
80        self.children.insert(command.name.clone(), command.clone());
81
82        for alias in command.aliases.clone() {
83            self.children.insert(alias, command.clone());
84        }
85
86        self
87    }
88
89    pub fn error<H: CommandErrorHandler<E, S> + 'static>(mut self, handler: H) -> Self {
90        self.error = Some(Arc::new(handler));
91
92        self
93    }
94
95    pub fn description<I: Into<String>>(mut self, description: I) -> Self {
96        self.description = Some(description.into());
97
98        self
99    }
100
101    pub fn signature<I: Into<String>>(mut self, signature: I) -> Self {
102        self.signature = Some(signature.into());
103
104        self
105    }
106
107    pub fn check<C: Check<E, S>>(mut self, check: C) -> Self {
108        self.checks.push(Arc::new(check));
109
110        self
111    }
112
113    pub fn alias<I: Into<String>>(mut self, alias: I) -> Self {
114        self.aliases.push(alias.into());
115
116        self
117    }
118
119    pub fn hidden(mut self) -> Self {
120        self.hidden = true;
121
122        self
123    }
124
125    pub fn children(&self) -> Vec<Command<E, S>> {
126        self.children
127            .clone()
128            .into_iter()
129            .filter(|(name, command)| name == &command.name)
130            .map(|(_, command)| command)
131            .collect()
132    }
133
134    pub fn get_command(&self, name: &str) -> Option<Command<E, S>> {
135        self.children.get(name).cloned()
136    }
137
138    pub async fn can_run(&self, context: Context<E, S>) -> Result<bool, E> {
139        for check in &self.checks {
140            if check.run(context.clone()).await? == false {
141                return Err(Error::CheckFailure.into());
142            }
143        }
144
145        Ok(true)
146    }
147
148    pub async fn invoke(&self, context: Context<E, S>) -> Result<(), E> {
149        (self.handle).handle(context).await
150    }
151}
152
153#[async_trait]
154pub trait CommandHandle<
155    T,
156    E: From<Error> + Clone + Debug + Send + Sync + 'static,
157    S: Debug + Clone + Send + Sync + 'static,
158>: Send + Sync + 'static
159{
160    async fn handle(&self, context: Context<E, S>) -> Result<(), E>;
161}
162
163#[async_trait]
164impl<E, S, F> CommandHandle<(), E, S> for F
165where
166    E: From<Error> + Clone + Debug + Send + Sync + 'static,
167    S: Debug + Clone + Send + Sync + 'static,
168    F: AsyncFn1<Context<E, S>, Output = Result<(), E>> + Send + Sync + 'static,
169    F::OutputFuture: Send,
170{
171    async fn handle(&self, context: Context<E, S>) -> Result<(), E> {
172        (self)(context).await
173    }
174}
175
176#[async_trait]
177impl<T1, E, S, F> CommandHandle<(T1,), E, S> for F
178where
179    T1: Converter<E, S> + Send,
180    E: From<Error> + Clone + Debug + Send + Sync + 'static,
181    S: Debug + Clone + Send + Sync + 'static,
182    F: AsyncFn2<Context<E, S>, T1, Output = Result<(), E>> + Send + Sync + 'static,
183    F::OutputFuture: Send,
184{
185    async fn handle(&self, context: Context<E, S>) -> Result<(), E> {
186        let t1 = T1::from_context(&context).await?;
187        (self)(context, t1).await
188    }
189}
190
191#[async_trait]
192impl<T1, T2, E, S, F> CommandHandle<(T1, T2), E, S> for F
193where
194    T1: Converter<E, S> + Send,
195    T2: Converter<E, S> + Send,
196    E: From<Error> + Clone + Debug + Send + Sync + 'static,
197    S: Debug + Clone + Send + Sync + 'static,
198    F: AsyncFn3<Context<E, S>, T1, T2, Output = Result<(), E>> + Send + Sync + 'static,
199    F::OutputFuture: Send,
200{
201    async fn handle(&self, context: Context<E, S>) -> Result<(), E> {
202        let t1 = T1::from_context(&context).await?;
203        let t2 = T2::from_context(&context).await?;
204
205        (self)(context, t1, t2).await
206    }
207}
208
209#[async_trait]
210impl<T1, T2, T3, E, S, F> CommandHandle<(T1, T2, T3), E, S> for F
211where
212    T1: Converter<E, S> + Send,
213    T2: Converter<E, S> + Send,
214    T3: Converter<E, S> + Send,
215    E: From<Error> + Clone + Debug + Send + Sync + 'static,
216    S: Debug + Clone + Send + Sync + 'static,
217    F: AsyncFn4<Context<E, S>, T1, T2, T3, Output = Result<(), E>> + Send + Sync + 'static,
218    F::OutputFuture: Send,
219{
220    async fn handle(&self, context: Context<E, S>) -> Result<(), E> {
221        let t1 = T1::from_context(&context).await?;
222        let t2 = T2::from_context(&context).await?;
223        let t3 = T3::from_context(&context).await?;
224
225        (self)(context, t1, t2, t3).await
226    }
227}
228
229struct ErasedCommandHandler<
230    T: Send + Sync + 'static,
231    E: From<Error> + Clone + Debug + Send + Sync + 'static,
232    S: Debug + Clone + Send + Sync + 'static,
233    H: CommandHandle<T, E, S>,
234> {
235    handle: H,
236    _p: PhantomData<(T, E, S)>,
237}
238
239#[async_trait]
240impl<
241    T: Send + Sync + 'static,
242    E: From<Error> + Clone + Debug + Send + Sync + 'static,
243    S: Debug + Clone + Send + Sync + 'static,
244    H: CommandHandle<T, E, S>,
245> CommandHandle<(), E, S> for ErasedCommandHandler<T, E, S, H>
246{
247    async fn handle(&self, context: Context<E, S>) -> Result<(), E> {
248        self.handle.handle(context).await
249    }
250}
251
252#[async_trait]
253pub trait CommandErrorHandler<
254    E: From<Error> + Clone + Debug + Send + Sync + 'static,
255    S: Debug + Clone + Send + Sync + 'static,
256>: Send + Sync {
257    async fn handle(&self, context: Context<E, S>, error: E) -> Result<(), E>;
258}
259
260#[async_trait]
261impl<E, S, F> CommandErrorHandler<E, S> for F where
262    E: From<Error> + Clone + Debug + Send + Sync + 'static,
263    S: Debug + Clone + Send + Sync + 'static,
264    F: AsyncFn2<Context<E, S>, E, Output = Result<(), E>> + Send + Sync + 'static,
265    F::OutputFuture: Send
266{
267    async fn handle(&self, context: Context<E, S>, error: E) -> Result<(), E> {
268        (self)(context, error).await
269    }
270}