Make command execution async.
This commit is contained in:
parent
97d91704a1
commit
3c2a37c0f7
|
@ -1,17 +1,18 @@
|
||||||
use chronicle_dicebot::commands::Command;
|
use chronicle_dicebot::commands;
|
||||||
use chronicle_dicebot::context::Context;
|
use chronicle_dicebot::context::Context;
|
||||||
use chronicle_dicebot::db::Database;
|
use chronicle_dicebot::db::Database;
|
||||||
use chronicle_dicebot::error::BotError;
|
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 db = Database::new(&sled::open("test-db")?);
|
||||||
let input = std::env::args().skip(1).collect::<Vec<String>>().join(" ");
|
let input = std::env::args().skip(1).collect::<Vec<String>>().join(" ");
|
||||||
let command = match Command::parse(&input) {
|
let command = match commands::parse(&input) {
|
||||||
Ok(command) => command,
|
Ok(command) => command,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
let context = Context::new(&db, "roomid", "localuser", &input);
|
let context = Context::new(&db, "roomid", "localuser", &input);
|
||||||
println!("{}", command.execute(&context).plain());
|
println!("{}", command.execute(&context).await.plain());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ use chronicle_dicebot::state::DiceBotState;
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use log::error;
|
use log::error;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use tokio::prelude::*;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
|
|
@ -227,7 +227,7 @@ impl EventEmitter for DiceBot {
|
||||||
|
|
||||||
let ctx = Context::new(&self.db, &room_id.as_str(), &sender_username, &msg_body);
|
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(
|
let response = AnyMessageEventContent::RoomMessage(MessageEventContent::Notice(
|
||||||
NoticeMessageEventContent::html(cmd_result.plain, cmd_result.html),
|
NoticeMessageEventContent::html(cmd_result.plain, cmd_result.html),
|
||||||
));
|
));
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::dice::ElementExpression;
|
||||||
use crate::error::BotError;
|
use crate::error::BotError;
|
||||||
use crate::help::HelpTopic;
|
use crate::help::HelpTopic;
|
||||||
use crate::roll::Roll;
|
use crate::roll::Roll;
|
||||||
|
use async_trait::async_trait;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
@ -33,19 +34,21 @@ impl Execution {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Command {
|
#[async_trait]
|
||||||
fn execute(&self, ctx: &Context) -> Execution;
|
pub trait Command: Send + Sync {
|
||||||
|
async fn execute(&self, ctx: &Context) -> Execution;
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RollCommand(ElementExpression);
|
pub struct RollCommand(ElementExpression);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Command for RollCommand {
|
impl Command for RollCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"roll regular dice"
|
"roll regular dice"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self, _ctx: &Context) -> Execution {
|
async fn execute(&self, _ctx: &Context) -> Execution {
|
||||||
let roll = self.0.roll();
|
let roll = self.0.roll();
|
||||||
let plain = format!("Dice: {}\nResult: {}", self.0, roll);
|
let plain = format!("Dice: {}\nResult: {}", self.0, roll);
|
||||||
let html = format!(
|
let html = format!(
|
||||||
|
@ -58,12 +61,13 @@ impl Command for RollCommand {
|
||||||
|
|
||||||
pub struct PoolRollCommand(DicePool);
|
pub struct PoolRollCommand(DicePool);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Command for PoolRollCommand {
|
impl Command for PoolRollCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"roll dice pool"
|
"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 pool_with_ctx = DicePoolWithContext(&self.0, ctx);
|
||||||
let roll_result = pool_with_ctx.roll();
|
let roll_result = pool_with_ctx.roll();
|
||||||
|
|
||||||
|
@ -89,12 +93,13 @@ impl Command for PoolRollCommand {
|
||||||
|
|
||||||
pub struct HelpCommand(Option<HelpTopic>);
|
pub struct HelpCommand(Option<HelpTopic>);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Command for HelpCommand {
|
impl Command for HelpCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"help information"
|
"help information"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self, _ctx: &Context) -> Execution {
|
async fn execute(&self, _ctx: &Context) -> Execution {
|
||||||
let help = match &self.0 {
|
let help = match &self.0 {
|
||||||
Some(topic) => topic.message(),
|
Some(topic) => topic.message(),
|
||||||
_ => "There is no help for this topic",
|
_ => "There is no help for this topic",
|
||||||
|
@ -108,12 +113,13 @@ impl Command for HelpCommand {
|
||||||
|
|
||||||
pub struct GetVariableCommand(String);
|
pub struct GetVariableCommand(String);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Command for GetVariableCommand {
|
impl Command for GetVariableCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"retrieve variable value"
|
"retrieve variable value"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self, ctx: &Context) -> Execution {
|
async fn execute(&self, ctx: &Context) -> Execution {
|
||||||
let name = &self.0;
|
let name = &self.0;
|
||||||
let value = match ctx.db.get_user_variable(&ctx.room_id, &ctx.username, name) {
|
let value = match ctx.db.get_user_variable(&ctx.room_id, &ctx.username, name) {
|
||||||
Ok(num) => format!("{} = {}", name, num),
|
Ok(num) => format!("{} = {}", name, num),
|
||||||
|
@ -129,12 +135,13 @@ impl Command for GetVariableCommand {
|
||||||
|
|
||||||
pub struct SetVariableCommand(String, i32);
|
pub struct SetVariableCommand(String, i32);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Command for SetVariableCommand {
|
impl Command for SetVariableCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"set variable value"
|
"set variable value"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self, ctx: &Context) -> Execution {
|
async fn execute(&self, ctx: &Context) -> Execution {
|
||||||
let name = &self.0;
|
let name = &self.0;
|
||||||
let value = self.1;
|
let value = self.1;
|
||||||
let result = ctx
|
let result = ctx
|
||||||
|
@ -154,12 +161,13 @@ impl Command for SetVariableCommand {
|
||||||
|
|
||||||
pub struct DeleteVariableCommand(String);
|
pub struct DeleteVariableCommand(String);
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
impl Command for DeleteVariableCommand {
|
impl Command for DeleteVariableCommand {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"delete variable"
|
"delete variable"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self, ctx: &Context) -> Execution {
|
async fn execute(&self, ctx: &Context) -> Execution {
|
||||||
let name = &self.0;
|
let name = &self.0;
|
||||||
let value = match ctx
|
let value = match ctx
|
||||||
.db
|
.db
|
||||||
|
@ -176,17 +184,15 @@ impl Command for DeleteVariableCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Command {
|
/// Parse a command string into a dynamic command execution trait
|
||||||
/// Parse a command string into a dynamic command execution trait
|
/// object. Returns an error if a command was recognized but not
|
||||||
/// object. Returns an error if a command was recognized but not
|
/// parsed correctly. Returns Ok(None) if no command was recognized.
|
||||||
/// parsed correctly. Returns Ok(None) if no command was recognized.
|
pub fn parse(s: &str) -> Result<Box<dyn Command>, BotError> {
|
||||||
pub fn parse(s: &str) -> Result<Box<dyn Command>, BotError> {
|
|
||||||
match parser::parse_command(s) {
|
match parser::parse_command(s) {
|
||||||
Ok(Some(command)) => Ok(command),
|
Ok(Some(command)) => Ok(command),
|
||||||
Ok(None) => Err(BotError::CommandError(CommandError::IgnoredCommand)),
|
Ok(None) => Err(BotError::CommandError(CommandError::IgnoredCommand)),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommandResult {
|
pub struct CommandResult {
|
||||||
|
@ -198,14 +204,14 @@ pub struct CommandResult {
|
||||||
/// go back to Matrix, if the command was executed (successfully or
|
/// go back to Matrix, if the command was executed (successfully or
|
||||||
/// not). If a command is determined to be ignored, this function will
|
/// not). If a command is determined to be ignored, this function will
|
||||||
/// return None, signifying that we should not send a response.
|
/// return None, signifying that we should not send a response.
|
||||||
pub fn execute_command(ctx: &Context) -> Option<CommandResult> {
|
pub async fn execute_command(ctx: &Context) -> Option<CommandResult> {
|
||||||
let res = Command::parse(&ctx.message_body).map(|cmd| {
|
let res = parse(&ctx.message_body);
|
||||||
let execution = cmd.execute(ctx);
|
|
||||||
(execution.plain().into(), execution.html().into())
|
|
||||||
});
|
|
||||||
|
|
||||||
let (plain, html) = match res {
|
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(BotError::CommandError(CommandError::IgnoredCommand)) => return None,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let message = format!("Error parsing command: {}", e);
|
let message = format!("Error parsing command: {}", e);
|
||||||
|
|
Loading…
Reference in New Issue