From 3c2a37c0f7bca9a019bc23591fb8d3d397a432c1 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Sat, 17 Oct 2020 15:47:17 +0000 Subject: [PATCH] Make command execution async. --- src/bin/dicebot-cmd.rs | 9 +++---- src/bin/dicebot.rs | 1 - src/bot.rs | 2 +- src/commands.rs | 54 +++++++++++++++++++++++------------------- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/bin/dicebot-cmd.rs b/src/bin/dicebot-cmd.rs index 6ba2697..645046a 100644 --- a/src/bin/dicebot-cmd.rs +++ b/src/bin/dicebot-cmd.rs @@ -1,17 +1,18 @@ -use chronicle_dicebot::commands::Command; +use chronicle_dicebot::commands; use chronicle_dicebot::context::Context; use chronicle_dicebot::db::Database; use chronicle_dicebot::error::BotError; -fn main() -> Result<(), BotError> { +#[tokio::main] +async fn main() -> Result<(), BotError> { let db = Database::new(&sled::open("test-db")?); let input = std::env::args().skip(1).collect::>().join(" "); - let command = match Command::parse(&input) { + let command = match commands::parse(&input) { Ok(command) => command, Err(e) => return Err(e), }; let context = Context::new(&db, "roomid", "localuser", &input); - println!("{}", command.execute(&context).plain()); + println!("{}", command.execute(&context).await.plain()); Ok(()) } diff --git a/src/bin/dicebot.rs b/src/bin/dicebot.rs index ad2e9e3..df886c1 100644 --- a/src/bin/dicebot.rs +++ b/src/bin/dicebot.rs @@ -8,7 +8,6 @@ use chronicle_dicebot::state::DiceBotState; use env_logger::Env; use log::error; use std::sync::{Arc, RwLock}; -use tokio::prelude::*; #[tokio::main] async fn main() { diff --git a/src/bot.rs b/src/bot.rs index 20aad35..ce83fc8 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -227,7 +227,7 @@ impl EventEmitter for DiceBot { let ctx = Context::new(&self.db, &room_id.as_str(), &sender_username, &msg_body); - if let Some(cmd_result) = execute_command(&ctx) { + if let Some(cmd_result) = execute_command(&ctx).await { let response = AnyMessageEventContent::RoomMessage(MessageEventContent::Notice( NoticeMessageEventContent::html(cmd_result.plain, cmd_result.html), )); diff --git a/src/commands.rs b/src/commands.rs index 1c808eb..7edfe3d 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -5,6 +5,7 @@ use crate::dice::ElementExpression; use crate::error::BotError; use crate::help::HelpTopic; use crate::roll::Roll; +use async_trait::async_trait; use thiserror::Error; pub mod parser; @@ -33,19 +34,21 @@ impl Execution { } } -pub trait Command { - fn execute(&self, ctx: &Context) -> Execution; +#[async_trait] +pub trait Command: Send + Sync { + async fn execute(&self, ctx: &Context) -> Execution; fn name(&self) -> &'static str; } pub struct RollCommand(ElementExpression); +#[async_trait] impl Command for RollCommand { fn name(&self) -> &'static str { "roll regular dice" } - fn execute(&self, _ctx: &Context) -> Execution { + async fn execute(&self, _ctx: &Context) -> Execution { let roll = self.0.roll(); let plain = format!("Dice: {}\nResult: {}", self.0, roll); let html = format!( @@ -58,12 +61,13 @@ impl Command for RollCommand { pub struct PoolRollCommand(DicePool); +#[async_trait] impl Command for PoolRollCommand { fn name(&self) -> &'static str { "roll dice pool" } - fn execute(&self, ctx: &Context) -> Execution { + async fn execute(&self, ctx: &Context) -> Execution { let pool_with_ctx = DicePoolWithContext(&self.0, ctx); let roll_result = pool_with_ctx.roll(); @@ -89,12 +93,13 @@ impl Command for PoolRollCommand { pub struct HelpCommand(Option); +#[async_trait] impl Command for HelpCommand { fn name(&self) -> &'static str { "help information" } - fn execute(&self, _ctx: &Context) -> Execution { + async fn execute(&self, _ctx: &Context) -> Execution { let help = match &self.0 { Some(topic) => topic.message(), _ => "There is no help for this topic", @@ -108,12 +113,13 @@ impl Command for HelpCommand { pub struct GetVariableCommand(String); +#[async_trait] impl Command for GetVariableCommand { fn name(&self) -> &'static str { "retrieve variable value" } - fn execute(&self, ctx: &Context) -> Execution { + async fn execute(&self, ctx: &Context) -> Execution { let name = &self.0; let value = match ctx.db.get_user_variable(&ctx.room_id, &ctx.username, name) { Ok(num) => format!("{} = {}", name, num), @@ -129,12 +135,13 @@ impl Command for GetVariableCommand { pub struct SetVariableCommand(String, i32); +#[async_trait] impl Command for SetVariableCommand { fn name(&self) -> &'static str { "set variable value" } - fn execute(&self, ctx: &Context) -> Execution { + async fn execute(&self, ctx: &Context) -> Execution { let name = &self.0; let value = self.1; let result = ctx @@ -154,12 +161,13 @@ impl Command for SetVariableCommand { pub struct DeleteVariableCommand(String); +#[async_trait] impl Command for DeleteVariableCommand { fn name(&self) -> &'static str { "delete variable" } - fn execute(&self, ctx: &Context) -> Execution { + async fn execute(&self, ctx: &Context) -> Execution { let name = &self.0; let value = match ctx .db @@ -176,16 +184,14 @@ impl Command for DeleteVariableCommand { } } -impl dyn Command { - /// Parse a command string into a dynamic command execution trait - /// object. Returns an error if a command was recognized but not - /// parsed correctly. Returns Ok(None) if no command was recognized. - pub fn parse(s: &str) -> Result, BotError> { - match parser::parse_command(s) { - Ok(Some(command)) => Ok(command), - Ok(None) => Err(BotError::CommandError(CommandError::IgnoredCommand)), - Err(e) => Err(e), - } +/// Parse a command string into a dynamic command execution trait +/// object. Returns an error if a command was recognized but not +/// parsed correctly. Returns Ok(None) if no command was recognized. +pub fn parse(s: &str) -> Result, BotError> { + match parser::parse_command(s) { + Ok(Some(command)) => Ok(command), + Ok(None) => Err(BotError::CommandError(CommandError::IgnoredCommand)), + Err(e) => Err(e), } } @@ -198,14 +204,14 @@ pub struct CommandResult { /// go back to Matrix, if the command was executed (successfully or /// not). If a command is determined to be ignored, this function will /// return None, signifying that we should not send a response. -pub fn execute_command(ctx: &Context) -> Option { - let res = Command::parse(&ctx.message_body).map(|cmd| { - let execution = cmd.execute(ctx); - (execution.plain().into(), execution.html().into()) - }); +pub async fn execute_command(ctx: &Context) -> Option { + let res = parse(&ctx.message_body); let (plain, html) = match res { - Ok(plain_and_html) => plain_and_html, + Ok(cmd) => { + let execution = cmd.execute(ctx).await; + (execution.plain().into(), execution.html().into()) + } Err(BotError::CommandError(CommandError::IgnoredCommand)) => return None, Err(e) => { let message = format!("Error parsing command: {}", e);