Localize all command parsing code into trait impls.
This cleans up the command parser a lot, as all of the one or two line functions and associated imports have been removed. Unfortunately it does make the command files larger, as two trait impls are required: one for converting to Box<dyn Command>, and one for converting from &str to the command type. Fixes #66.
This commit is contained in:
parent
5d002e5063
commit
b05129ad9f
|
@ -1,11 +1,37 @@
|
|||
use super::{Command, Execution, ExecutionResult};
|
||||
use crate::basic::dice::ElementExpression;
|
||||
use crate::basic::parser::parse_element_expression;
|
||||
use crate::basic::roll::Roll;
|
||||
use crate::context::Context;
|
||||
use crate::error::BotError;
|
||||
use async_trait::async_trait;
|
||||
use nom::Err as NomErr;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct RollCommand(pub ElementExpression);
|
||||
|
||||
impl From<RollCommand> for Box<dyn Command> {
|
||||
fn from(cmd: RollCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for RollCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
let result = parse_element_expression(input);
|
||||
match result {
|
||||
Ok((rest, expression)) if rest.len() == 0 => Ok(RollCommand(expression)),
|
||||
//"Legacy code boundary": translates Nom errors into BotErrors.
|
||||
Ok(_) => Err(BotError::NomParserIncomplete),
|
||||
Err(NomErr::Error(e)) => Err(BotError::NomParserError(e.1)),
|
||||
Err(NomErr::Failure(e)) => Err(BotError::NomParserError(e.1)),
|
||||
Err(NomErr::Incomplete(_)) => Err(BotError::NomParserIncomplete),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for RollCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
|
|
@ -1,10 +1,35 @@
|
|||
use super::{Command, Execution, ExecutionResult};
|
||||
use crate::cofd::dice::{roll_pool, DicePool, DicePoolWithContext};
|
||||
use crate::cofd::parser::{create_chance_die, parse_dice_pool};
|
||||
use crate::context::Context;
|
||||
use crate::error::BotError;
|
||||
use async_trait::async_trait;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct PoolRollCommand(pub DicePool);
|
||||
|
||||
impl PoolRollCommand {
|
||||
pub fn chance_die() -> Result<PoolRollCommand, BotError> {
|
||||
let pool = create_chance_die()?;
|
||||
Ok(PoolRollCommand(pool))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PoolRollCommand> for Box<dyn Command> {
|
||||
fn from(cmd: PoolRollCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for PoolRollCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
let pool = parse_dice_pool(input)?;
|
||||
Ok(PoolRollCommand(pool))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for PoolRollCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
|
|
@ -4,10 +4,28 @@ use crate::cthulhu::dice::{
|
|||
advancement_roll, regular_roll, AdvancementRoll, AdvancementRollWithContext, DiceRoll,
|
||||
DiceRollWithContext,
|
||||
};
|
||||
use crate::cthulhu::parser::{parse_advancement_roll, parse_regular_roll};
|
||||
use crate::error::BotError;
|
||||
use async_trait::async_trait;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct CthRoll(pub DiceRoll);
|
||||
|
||||
impl From<CthRoll> for Box<dyn Command> {
|
||||
fn from(cmd: CthRoll) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for CthRoll {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
let roll = parse_regular_roll(input)?;
|
||||
Ok(CthRoll(roll))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for CthRoll {
|
||||
fn name(&self) -> &'static str {
|
||||
|
@ -33,6 +51,21 @@ impl Command for CthRoll {
|
|||
|
||||
pub struct CthAdvanceRoll(pub AdvancementRoll);
|
||||
|
||||
impl From<CthAdvanceRoll> for Box<dyn Command> {
|
||||
fn from(cmd: CthAdvanceRoll) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for CthAdvanceRoll {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
let roll = parse_advancement_roll(input)?;
|
||||
Ok(CthAdvanceRoll(roll))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for CthAdvanceRoll {
|
||||
fn name(&self) -> &'static str {
|
||||
|
|
|
@ -1,13 +1,28 @@
|
|||
use super::{Command, Execution, ExecutionResult};
|
||||
use crate::context::Context;
|
||||
use crate::db::Users;
|
||||
use crate::error::BotError::{AccountDoesNotExist, AuthenticationError, PasswordCreationError};
|
||||
use crate::logic::hash_password;
|
||||
use crate::models::{AccountStatus, User};
|
||||
use crate::{context::Context, error::BotError};
|
||||
use async_trait::async_trait;
|
||||
use std::convert::{Into, TryFrom};
|
||||
|
||||
pub struct RegisterCommand(pub String);
|
||||
|
||||
impl From<RegisterCommand> for Box<dyn Command> {
|
||||
fn from(cmd: RegisterCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for RegisterCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Ok(RegisterCommand(value.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for RegisterCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
@ -38,6 +53,20 @@ impl Command for RegisterCommand {
|
|||
|
||||
pub struct CheckCommand(pub String);
|
||||
|
||||
impl From<CheckCommand> for Box<dyn Command> {
|
||||
fn from(cmd: CheckCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for CheckCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Ok(CheckCommand(value.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for CheckCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
@ -60,6 +89,20 @@ impl Command for CheckCommand {
|
|||
|
||||
pub struct UnregisterCommand;
|
||||
|
||||
impl From<UnregisterCommand> for Box<dyn Command> {
|
||||
fn from(cmd: UnregisterCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for UnregisterCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(_: &str) -> Result<Self, Self::Error> {
|
||||
Ok(UnregisterCommand)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for UnregisterCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
|
|
@ -1,10 +1,27 @@
|
|||
use super::{Command, Execution, ExecutionResult};
|
||||
use crate::context::Context;
|
||||
use crate::help::HelpTopic;
|
||||
use crate::error::BotError;
|
||||
use crate::help::{parse_help_topic, HelpTopic};
|
||||
use async_trait::async_trait;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct HelpCommand(pub Option<HelpTopic>);
|
||||
|
||||
impl From<HelpCommand> for Box<dyn Command> {
|
||||
fn from(cmd: HelpCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for HelpCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
let topic = parse_help_topic(input);
|
||||
Ok(HelpCommand(topic))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for HelpCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
* governed by the terms of the MIT license, from the original
|
||||
* axfive-matrix-dicebot project.
|
||||
*/
|
||||
use crate::basic::parser::parse_element_expression;
|
||||
use crate::cofd::parser::{create_chance_die, parse_dice_pool};
|
||||
use crate::commands::{
|
||||
basic_rolling::RollCommand,
|
||||
cofd::PoolRollCommand,
|
||||
|
@ -16,13 +14,10 @@ use crate::commands::{
|
|||
},
|
||||
Command,
|
||||
};
|
||||
use crate::cthulhu::parser::{parse_advancement_roll, parse_regular_roll};
|
||||
use crate::error::BotError;
|
||||
use crate::help::parse_help_topic;
|
||||
use crate::parser::variables::parse_set_variable;
|
||||
use combine::parser::char::{char, letter, space};
|
||||
use combine::{any, many1, optional, Parser};
|
||||
use nom::Err as NomErr;
|
||||
use std::convert::TryFrom;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Error)]
|
||||
|
@ -34,73 +29,6 @@ pub enum CommandParsingError {
|
|||
InternalParseError(#[from] combine::error::StringStreamError),
|
||||
}
|
||||
|
||||
// Parse a roll expression.
|
||||
fn parse_roll(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
let result = parse_element_expression(input);
|
||||
match result {
|
||||
Ok((rest, expression)) if rest.len() == 0 => Ok(Box::new(RollCommand(expression))),
|
||||
//Legacy code boundary translates nom errors into BotErrors.
|
||||
Ok(_) => Err(BotError::NomParserIncomplete),
|
||||
Err(NomErr::Error(e)) => Err(BotError::NomParserError(e.1)),
|
||||
Err(NomErr::Failure(e)) => Err(BotError::NomParserError(e.1)),
|
||||
Err(NomErr::Incomplete(_)) => Err(BotError::NomParserIncomplete),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_register_command(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
Ok(Box::new(RegisterCommand(input.to_owned())))
|
||||
}
|
||||
|
||||
fn parse_check_command(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
Ok(Box::new(CheckCommand(input.to_owned())))
|
||||
}
|
||||
|
||||
fn parse_unregister_command() -> Result<Box<dyn Command>, BotError> {
|
||||
Ok(Box::new(UnregisterCommand))
|
||||
}
|
||||
|
||||
fn parse_get_variable_command(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
Ok(Box::new(GetVariableCommand(input.to_owned())))
|
||||
}
|
||||
|
||||
fn parse_set_variable_command(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
let (variable_name, value) = parse_set_variable(input)?;
|
||||
Ok(Box::new(SetVariableCommand(variable_name, value)))
|
||||
}
|
||||
|
||||
fn parse_delete_variable_command(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
Ok(Box::new(DeleteVariableCommand(input.to_owned())))
|
||||
}
|
||||
|
||||
fn parse_pool_roll(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
let pool = parse_dice_pool(input)?;
|
||||
Ok(Box::new(PoolRollCommand(pool)))
|
||||
}
|
||||
|
||||
fn parse_cth_roll(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
let roll = parse_regular_roll(input)?;
|
||||
Ok(Box::new(CthRoll(roll)))
|
||||
}
|
||||
|
||||
fn parse_cth_advancement_roll(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
let roll = parse_advancement_roll(input)?;
|
||||
Ok(Box::new(CthAdvanceRoll(roll)))
|
||||
}
|
||||
|
||||
fn chance_die() -> Result<Box<dyn Command>, BotError> {
|
||||
let pool = create_chance_die()?;
|
||||
Ok(Box::new(PoolRollCommand(pool)))
|
||||
}
|
||||
|
||||
fn get_all_variables() -> Result<Box<dyn Command>, BotError> {
|
||||
Ok(Box::new(GetAllVariablesCommand))
|
||||
}
|
||||
|
||||
fn help(topic: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
let topic = parse_help_topic(topic);
|
||||
Ok(Box::new(HelpCommand(topic)))
|
||||
}
|
||||
|
||||
/// Split an input string into its constituent command and "everything
|
||||
/// else" parts. Extracts the command separately from its input (i.e.
|
||||
/// rest of the line) and returns a tuple of (command_input, command).
|
||||
|
@ -132,25 +60,34 @@ fn split_command(input: &str) -> Result<(String, String), CommandParsingError> {
|
|||
Ok((command, command_input))
|
||||
}
|
||||
|
||||
/// Atempt to convert text input to a Boxed command type. Shortens
|
||||
/// boilerplate.
|
||||
macro_rules! convert_to {
|
||||
($type:ident, $input: expr) => {
|
||||
$type::try_from($input.as_str()).map(Into::into)
|
||||
};
|
||||
}
|
||||
|
||||
/// Potentially parse a command expression. If we recognize the
|
||||
/// command, an error should be raised if the command is misparsed. If
|
||||
/// we don't recognize the command, return an error.
|
||||
pub fn parse_command(input: &str) -> Result<Box<dyn Command>, BotError> {
|
||||
match split_command(input) {
|
||||
Ok((cmd, cmd_input)) => match cmd.to_lowercase().as_ref() {
|
||||
"variables" => get_all_variables(),
|
||||
"get" => parse_get_variable_command(&cmd_input),
|
||||
"set" => parse_set_variable_command(&cmd_input),
|
||||
"del" => parse_delete_variable_command(&cmd_input),
|
||||
"r" | "roll" => parse_roll(&cmd_input),
|
||||
"rp" | "pool" => parse_pool_roll(&cmd_input),
|
||||
"cthroll" => parse_cth_roll(&cmd_input),
|
||||
"cthadv" | "ctharoll" => parse_cth_advancement_roll(&cmd_input),
|
||||
"chance" => chance_die(),
|
||||
"help" => help(&cmd_input),
|
||||
"register" => parse_register_command(&cmd_input),
|
||||
"check" => parse_check_command(&cmd_input),
|
||||
"unregister" => parse_unregister_command(),
|
||||
// "variables" => GetAllVariablesCommand::try_from(input).map(Into::into),
|
||||
"variables" => convert_to!(GetAllVariablesCommand, cmd_input),
|
||||
"get" => convert_to!(GetVariableCommand, cmd_input),
|
||||
"set" => convert_to!(SetVariableCommand, cmd_input),
|
||||
"del" => convert_to!(DeleteVariableCommand, cmd_input),
|
||||
"r" | "roll" => convert_to!(RollCommand, cmd_input),
|
||||
"rp" | "pool" => convert_to!(PoolRollCommand, cmd_input),
|
||||
"chance" => PoolRollCommand::chance_die().map(Into::into),
|
||||
"cthroll" => convert_to!(CthRoll, cmd_input),
|
||||
"cthadv" | "ctharoll" => convert_to!(CthAdvanceRoll, cmd_input),
|
||||
"help" => convert_to!(HelpCommand, cmd_input),
|
||||
"register" => convert_to!(RegisterCommand, cmd_input),
|
||||
"check" => convert_to!(CheckCommand, cmd_input),
|
||||
"unregister" => convert_to!(UnregisterCommand, cmd_input),
|
||||
_ => Err(CommandParsingError::UnrecognizedCommand(cmd).into()),
|
||||
},
|
||||
//All other errors passed up.
|
||||
|
|
|
@ -2,10 +2,26 @@ use super::{Command, Execution, ExecutionResult};
|
|||
use crate::context::Context;
|
||||
use crate::db::errors::DataError;
|
||||
use crate::db::Variables;
|
||||
use crate::error::BotError;
|
||||
use async_trait::async_trait;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct GetAllVariablesCommand;
|
||||
|
||||
impl From<GetAllVariablesCommand> for Box<dyn Command> {
|
||||
fn from(cmd: GetAllVariablesCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for GetAllVariablesCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(_: &str) -> Result<Self, Self::Error> {
|
||||
Ok(GetAllVariablesCommand)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for GetAllVariablesCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
@ -41,6 +57,20 @@ impl Command for GetAllVariablesCommand {
|
|||
|
||||
pub struct GetVariableCommand(pub String);
|
||||
|
||||
impl From<GetVariableCommand> for Box<dyn Command> {
|
||||
fn from(cmd: GetVariableCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for GetVariableCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
Ok(GetVariableCommand(input.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for GetVariableCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
@ -71,6 +101,21 @@ impl Command for GetVariableCommand {
|
|||
|
||||
pub struct SetVariableCommand(pub String, pub i32);
|
||||
|
||||
impl From<SetVariableCommand> for Box<dyn Command> {
|
||||
fn from(cmd: SetVariableCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for SetVariableCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
let (variable_name, value) = crate::parser::variables::parse_set_variable(input)?;
|
||||
Ok(SetVariableCommand(variable_name, value))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for SetVariableCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
@ -97,6 +142,20 @@ impl Command for SetVariableCommand {
|
|||
|
||||
pub struct DeleteVariableCommand(pub String);
|
||||
|
||||
impl From<DeleteVariableCommand> for Box<dyn Command> {
|
||||
fn from(cmd: DeleteVariableCommand) -> Self {
|
||||
Box::new(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for DeleteVariableCommand {
|
||||
type Error = BotError;
|
||||
|
||||
fn try_from(input: &str) -> Result<Self, Self::Error> {
|
||||
Ok(DeleteVariableCommand(input.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Command for DeleteVariableCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
|
|
Loading…
Reference in New Issue