titanium_rs/
framework.rs

1use ahash::AHashMap;
2use std::future::Future;
3use std::pin::Pin;
4
5use crate::context::Context;
6
7use crate::error::TitaniumError;
8
9type CommandHandler = Box<
10    dyn Fn(Context) -> Pin<Box<dyn Future<Output = Result<(), TitaniumError>> + Send>> + Send + Sync,
11>;
12type ErrorHandler =
13    Box<dyn Fn(TitaniumError, Context) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
14
15pub struct Framework {
16    pub commands: AHashMap<String, CommandHandler>,
17    pub on_error: Option<ErrorHandler>,
18}
19
20impl Default for Framework {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl Framework {
27    pub fn new() -> Self {
28        Self {
29            commands: AHashMap::new(),
30            on_error: None,
31        }
32    }
33
34    pub fn command<F, Fut>(mut self, name: &str, handler: F) -> Self
35    where
36        F: Fn(Context) -> Fut + Send + Sync + 'static,
37        Fut: Future<Output = Result<(), TitaniumError>> + Send + 'static,
38    {
39        self.commands.insert(
40            name.to_string(),
41            Box::new(move |ctx| Box::pin(handler(ctx))),
42        );
43        self
44    }
45
46    /// Set a global error handler for commands.
47    pub fn on_error<F, Fut>(mut self, handler: F) -> Self
48    where
49        F: Fn(TitaniumError, Context) -> Fut + Send + Sync + 'static,
50        Fut: Future<Output = ()> + Send + 'static,
51    {
52        self.on_error = Some(Box::new(move |err, ctx| Box::pin(handler(err, ctx))));
53        self
54    }
55
56    /// Dispatch a command.
57    pub async fn dispatch(&self, name: &str, ctx: Context) {
58        if let Some(handler) = self.commands.get(name) {
59            // We need to clone context for error handler if needed,
60            // but Context is cheap to clone (Arc internal).
61            let ctx_clone = ctx.clone();
62
63            if let Err(err) = handler(ctx).await {
64                if let Some(error_handler) = &self.on_error {
65                    error_handler(err, ctx_clone).await;
66                } else {
67                    // Default error logging
68                    eprintln!("Command execution failed: {:?}", err);
69                }
70            }
71        }
72    }
73}