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