Implement basic, not-well-formatted help.

This commit is contained in:
projectmoon 2020-08-28 22:02:41 +00:00
parent 8484e9ffde
commit 531844fbb7
6 changed files with 157 additions and 8 deletions

16
Cargo.lock generated
View File

@ -172,6 +172,7 @@ dependencies = [
"async-trait",
"dirs",
"env_logger",
"indoc",
"itertools",
"log",
"matrix-sdk",
@ -680,6 +681,15 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "indoc"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644defcefee68d7805653a682e99a2e2a5014a1fc3cc9be7059a215844eeea6f"
dependencies = [
"unindent",
]
[[package]]
name = "instant"
version = "0.1.6"
@ -2051,6 +2061,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "unindent"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af41d708427f8fd0e915dcebb2cae0f0e6acb2a939b2d399c265c39a38a18942"
[[package]]
name = "url"
version = "2.1.1"

View File

@ -21,6 +21,7 @@ itertools = "0.9"
async-trait = "0.1"
url = "2.1"
dirs = "3.0"
indoc = "1.0"
# The versioning of the matrix SDK follows its Cargo.toml. The SDK and
# macros are on master, but it imports the common and base from 0.1.0.

View File

@ -1,5 +1,6 @@
use crate::cofd::dice::DicePool;
use crate::dice::ElementExpression;
use crate::help::HelpTopic;
use crate::parser::trim;
use crate::roll::Roll;
@ -61,10 +62,29 @@ impl Command for PoolRollCommand {
}
}
/// Parse a command string into a dynamic command execution trait object.
/// Returns an error if a command was recognized but not parsed correctly. Returns None if no
/// command was recognized.
pub fn parse_command(s: &str) -> Result<Option<Box<dyn Command>>, String> {
pub struct HelpCommand(Option<HelpTopic>);
impl Command for HelpCommand {
fn name(&self) -> &'static str {
"help information"
}
fn execute(&self) -> Execution {
let help = match &self.0 {
Some(topic) => topic.message(),
_ => "There is no help for this topic",
};
let plain = format!("Help: {}", help);
let html = format!("<p><strong>Help:</strong> {}", help.replace("\n", "<br/>"));
Execution { plain, html }
}
}
/// Parse a command string into a dynamic command execution trait
/// object. Returns an error if a command was recognized but not
/// parsed correctly. Returns None if no command was recognized.
pub fn parse_command<'a>(s: &'a str) -> Result<Option<Box<dyn Command + 'a>>, String> {
match parser::parse_command(s) {
Ok((input, command)) => match (input, &command) {
//This clause prevents bot from spamming messages to itself
@ -115,6 +135,19 @@ mod tests {
.expect("was error"));
}
#[test]
fn help_whitespace_test() {
assert!(parse_command("!help stuff ")
.map(|p| p.is_some())
.expect("was error"));
assert!(parse_command(" !help stuff")
.map(|p| p.is_some())
.expect("was error"));
assert!(parse_command(" !help stuff ")
.map(|p| p.is_some())
.expect("was error"));
}
#[test]
fn roll_whitespace_test() {
assert!(parse_command("!roll 1d4 + 5d6 -3 ")

View File

@ -1,9 +1,10 @@
use nom::{alt, complete, named, tag, take_while, tuple, IResult};
use crate::cofd::parser::{create_chance_die, parse_dice_pool};
use crate::commands::{Command, PoolRollCommand, RollCommand};
use crate::commands::{Command, HelpCommand, PoolRollCommand, RollCommand};
use crate::dice::parser::parse_element_expression;
use crate::parser::eat_whitespace;
use crate::help::parse_help_topic;
use crate::parser::{eat_whitespace, trim};
// Parse a roll expression.
fn parse_roll(input: &str) -> IResult<&str, Box<dyn Command>> {
@ -23,6 +24,12 @@ fn chance_die() -> IResult<&'static str, Box<dyn Command>> {
Ok((input, Box::new(PoolRollCommand(pool))))
}
fn help(topic: &str) -> IResult<&str, Box<dyn Command>> {
let (topic, _) = eat_whitespace(topic)?;
let topic = parse_help_topic(&trim(topic));
Ok(("", Box::new(HelpCommand(topic))))
}
/// 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, ignore it and return none
pub fn parse_command(original_input: &str) -> IResult<&str, Option<Box<dyn Command>>> {
@ -32,7 +39,9 @@ pub fn parse_command(original_input: &str) -> IResult<&str, Option<Box<dyn Comma
named!(command(&str) -> (&str, &str), tuple!(
complete!(tag!("!")),
alt!(
complete!(tag!("chance")) | //TODO figure out how to just have it read single commands.
//TODO figure out how to gracefully handle arbitrary single commands.
complete!(tag!("chance")) |
complete!(tag!("help")) |
complete!(take_while!(char::is_alphabetic))
)
));
@ -40,13 +49,16 @@ pub fn parse_command(original_input: &str) -> IResult<&str, Option<Box<dyn Comma
let (input, command) = match command(input) {
// Strip the exclamation mark
Ok((input, (_, result))) => (input, result),
Err(_e) => return Ok((original_input, None)),
Err(_e) => {
return Ok((original_input, None));
}
};
match command {
"r" | "roll" => parse_roll(input).map(|(input, command)| (input, Some(command))),
"rp" | "pool" => parse_pool_roll(input).map(|(input, command)| (input, Some(command))),
"chance" => chance_die().map(|(input, command)| (input, Some(command))),
"help" => help(input).map(|(input, command)| (input, Some(command))),
// No recognized command, ignore this.
_ => Ok((original_input, None)),
}

86
src/help.rs Normal file
View File

@ -0,0 +1,86 @@
use indoc::indoc;
pub fn parse_help_topic(input: &str) -> Option<HelpTopic> {
match input {
"cofd" => Some(HelpTopic::ChroniclesOfDarkness),
"dicepool" => Some(HelpTopic::DicePool),
"dice" => Some(HelpTopic::RollingDice),
"" => Some(HelpTopic::General),
_ => None,
}
}
pub enum HelpTopic {
ChroniclesOfDarkness,
DicePool,
RollingDice,
General,
}
const COFD_HELP: &'static str = indoc! {"
Chronicles of Darkness
Commands available:
!pool, !rp: roll a dice pool
!chance: roll a chance die
See also:
!help dicepool
"};
const DICE_HELP: &'static str = indoc! {"
Rolling basic dice
Command: !roll, !r
Syntax !roll <dice-expression>
Dice expression can be a basic die (e.g. 1d4), with a bonus (1d4+3),
or a more complex series of dice rolls or arbitrary numbers.
Parentheses are not supported.
Examples:
!roll 1d4
!roll 1d4+5
!roll 2d6+8
!roll 2d8 + 4d6 - 3
"};
const DICEPOOL_HELP: &'static str = indoc! {"
Rolling dice pools
Command: !pool, !rp
Syntax: !pool <num><modifiers>
Modifiers:
n = nine-again
e = eight-again
r = rote quality
x = do not re-roll 10s
s<num> = number of successes for exceptional
Examples:
!pool 8 (roll a regular pool of 8 dice)
!pool 5n (roll dice pool of 5, nine-again)
!pool 6rs3 (roll dice pool of 6, rote quality, 3 successes for exceptional)
"};
const GENERAL_HELP: &'static str = indoc! {"
General Help
Try these help commands:
!help cofd
!help dice
"};
impl HelpTopic {
pub fn message(&self) -> &str {
match self {
HelpTopic::ChroniclesOfDarkness => COFD_HELP,
HelpTopic::DicePool => DICEPOOL_HELP,
HelpTopic::RollingDice => DICE_HELP,
HelpTopic::General => GENERAL_HELP,
}
}
}

View File

@ -2,5 +2,6 @@ pub mod bot;
pub mod cofd;
pub mod commands;
pub mod dice;
mod help;
mod parser;
pub mod roll;