aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNathan Perry <np@nathanperry.dev>2024-08-07 07:39:16 -0400
committerNathan Perry <np@nathanperry.dev>2024-08-07 07:39:16 -0400
commitb58b03c22d637fe8f7200edb6953325bf359544d (patch)
tree606865b7afc4498b02da14895723b2b0ed49760e /src
parent96c197bde0f83d8b99ec66238856c76b41bfd5e1 (diff)
split calc into separate subcrate
Diffstat (limited to 'src')
-rw-r--r--src/bot.rs10
-rw-r--r--src/commands/calc.pest79
-rw-r--r--src/commands/game.rs5
-rw-r--r--src/commands/roll.rs212
-rw-r--r--src/db/mod.rs10
-rw-r--r--src/db/models.rs10
-rw-r--r--src/lib.rs3
-rw-r--r--src/log_setup.rs9
-rw-r--r--src/util/mod.rs20
9 files changed, 37 insertions, 321 deletions
diff --git a/src/bot.rs b/src/bot.rs
index 6a2ae72..4942c98 100644
--- a/src/bot.rs
+++ b/src/bot.rs
@@ -57,10 +57,8 @@ use crate::{
err_msg,
util,
util::OAUTH_URL,
- Error,
PoiseContext,
PoiseData,
- Result,
};
pub struct HttpKey;
@@ -151,8 +149,8 @@ lazy_static! {
let restrict_path = CONFIG.restrict.as_ref().unwrap_or(&default_path);
let restrict_ids = File::open(restrict_path)
- .map_err(Error::from)
- .and_then(|f| serde_json::from_reader::<_, Vec<u64>>(f).map_err(Error::from));
+ .map_err(anyhow::Error::from)
+ .and_then(|f| serde_json::from_reader::<_, Vec<u64>>(f).map_err(anyhow::Error::from));
if let Err(ref e) = restrict_ids {
warn!("opening restrict file: {}", e);
@@ -211,7 +209,7 @@ fn on_err(err: FrameworkError<PoiseData, anyhow::Error>) -> BoxFuture<()> {
fn noop<U, E>(
_ctx: PrefixContext<'_, U, E>,
- ) -> BoxFuture<core::result::Result<(), FrameworkError<U, E>>> {
+ ) -> BoxFuture<Result<(), FrameworkError<U, E>>> {
Box::pin(async { Ok(()) })
}
@@ -365,7 +363,7 @@ fn after_handle(ctx: PoiseContext) -> BoxFuture<()> {
})
}
-pub async fn run() -> Result<()> {
+pub async fn run() -> anyhow::Result<()> {
let token = &CONFIG.discord.auth.token;
let sb_config = songbird::Config::default();
diff --git a/src/commands/calc.pest b/src/commands/calc.pest
deleted file mode 100644
index 07eeddb..0000000
--- a/src/commands/calc.pest
+++ /dev/null
@@ -1,79 +0,0 @@
-num = {
- hex
- | oct
- | binary
- | float
-}
-
-float = @{ int ~ ( "." ~ ASCII_DIGIT*)? ~ (^"e" ~ int)? }
- int = { "-"? ~ ASCII_DIGIT+ }
-
-hex = @{ "0x" ~ ASCII_HEX_DIGIT+ }
-oct = @{ "0o" ~ ASCII_OCT_DIGIT+ }
-binary = @{ "0b" ~ ASCII_BIN_DIGIT+ }
-
-infix = _{ add | sub | mul | div | modulo }
- add = { "+" }
- sub = { "-" }
- modulo = { "%" | "mod" }
- mul = { "*" }
- div = { "/" }
-
-tight_infix = _{ dice | pow }
- dice = { "d" }
- pow = { "^" }
-
-trig = _{ sin | cos | tan | asin | acos | atan }
- sin = { "sin" }
- cos = { "cos" }
- tan = { "tan" }
- asin = { "asin" }
- acos = { "acos" }
- atan = { "atan" }
-
-htrig = _{ sinh | cosh | tanh | asinh | acosh | atanh }
- sinh = { "sinh" }
- cosh = { "cosh" }
- tanh = { "tanh" }
- asinh = { "asinh" }
- acosh = { "acosh" }
- atanh = { "atanh" }
-
-unary_prefix = _{ log | sqrt | sgn | htrig | trig | exp | abs | ceil | floor | round }
- log = { "log" | "ln" }
- sqrt = { "sqrt" }
- sgn = { "sgn" }
- exp = { "exp" }
- abs = { "abs" }
- ceil = { "ceil" }
- floor = { "floor" }
- round = { "round" }
-
-binary_prefix = _{ min | max | atan2 }
- min = { "min" }
- max = { "max" }
- atan2 = { "atan2" }
-
-suffix = _{ factorial }
- factorial = { "!" }
-
-term = _{ num | "(" ~ expr ~ ")" }
-
-suffix_expr = { term ~ suffix }
-unary_expr = ${ unary_prefix ~ ws+ ~ outfix_expr }
-binary_expr = ${ binary_prefix ~ ws+ ~ outfix_expr ~ ws+ ~ outfix_expr }
-
-tight = _{ (suffix_expr | term) ~ (tight_infix ~ tight)* }
-
-expr = { outfix_expr ~ (infix ~ outfix_expr)* }
-
-outfix_expr = _{
- tight |
- binary_expr |
- unary_expr
-}
-
-calc = _{ SOI ~ expr ~ EOI }
-
-ws = _{ " " | "\t" | "\n" }
-WHITESPACE = _{ ws }
diff --git a/src/commands/game.rs b/src/commands/game.rs
index 3b16fd3..ba9ac69 100644
--- a/src/commands/game.rs
+++ b/src/commands/game.rs
@@ -36,7 +36,6 @@ use crate::{
bot::HttpKey,
util,
PoiseContext,
- Result,
CONFIG,
};
@@ -125,7 +124,7 @@ enum GameStatus {
impl FromStr for GameStatus {
type Err = anyhow::Error;
- fn from_str(s: &str) -> Result<Self> {
+ fn from_str(s: &str) -> anyhow::Result<Self> {
use std::char;
if s.starts_with('y') {
@@ -404,7 +403,7 @@ async fn _game(
Ok(())
}
-async fn load_spreadsheet(client: &reqwest::Client) -> Result<Vec<Vec<String>>> {
+async fn load_spreadsheet(client: &reqwest::Client) -> anyhow::Result<Vec<Vec<String>>> {
let mut u = SPREADSHEET_URL.clone();
u.query_pairs_mut()
diff --git a/src/commands/roll.rs b/src/commands/roll.rs
index abd7466..fd3102c 100644
--- a/src/commands/roll.rs
+++ b/src/commands/roll.rs
@@ -1,214 +1,18 @@
-use lazy_static::lazy_static;
-use log::{
- debug,
- error,
-};
-use pest::iterators::Pair;
-use rand::prelude::*;
-use std::result::Result as StdResult;
-use thiserror::Error;
-
-use crate::{
- util,
- PoiseContext,
-};
-
-#[derive(pest_derive::Parser)]
-#[grammar = "commands/calc.pest"]
-struct Calc;
-
-#[derive(Copy, Clone, Error, Debug, PartialEq, Eq, Hash)]
-pub(crate) enum CalcError {
- #[error("pest was unable to parse the input")]
- Pest,
-
- #[error("invalid number format")]
- NumberFormat,
-
- #[error("bad argument count")]
- ArgCount,
-}
-
-impl Calc {
- fn eval<S: AsRef<str>>(s: S) -> StdResult<f64, CalcError> {
- use pest::{
- iterators::Pairs,
- pratt_parser::PrattParser,
- Parser,
- };
-
- use self::Rule::*;
-
- lazy_static! {
- static ref CLIMBER: PrattParser<Rule> = {
- use pest::pratt_parser::{
- Assoc::*,
- Op,
- };
-
- PrattParser::new()
- .op(Op::infix(add, Left) | Op::infix(sub, Left) | Op::infix(modulo, Left))
- .op(Op::infix(mul, Left) | Op::infix(div, Left))
- .op(Op::infix(dice, Left))
- .op(Op::infix(pow, Right))
- .op(Op::postfix(EOI)) // discarded below
- };
- }
-
- let result = Calc::parse(calc, s.as_ref()).map_err(|_| CalcError::Pest)?;
-
- fn eval_single_pair(pair: Pair<Rule>) -> StdResult<f64, CalcError> {
- let result = match pair.as_rule() {
- oct | hex | binary => {
- let base = match pair.as_rule() {
- hex => 16,
- oct => 8,
- binary => 2,
- _ => unreachable!(),
- };
-
- u64::from_str_radix(&pair.as_str()[2..], base)
- .map_err(|_| CalcError::NumberFormat)? as f64
- },
- float => pair.as_str().parse::<f64>().map_err(|_| CalcError::NumberFormat)?,
- expr | num => eval_expr(pair.into_inner())?,
- unary_expr => {
- let mut p = pair.into_inner();
-
- let op = p.next().ok_or(CalcError::ArgCount)?;
- let arg = eval_expr(p)?;
-
- match op.as_rule() {
- log => arg.ln(),
- sqrt => arg.sqrt(),
- sgn => arg.signum(),
-
- sin => arg.sin(),
- cos => arg.cos(),
- tan => arg.tan(),
- asin => arg.asin(),
- acos => arg.acos(),
- atan => arg.atan(),
-
- sinh => arg.sinh(),
- cosh => arg.cosh(),
- tanh => arg.tanh(),
- asinh => arg.asinh(),
- acosh => arg.acosh(),
- atanh => arg.atanh(),
-
- exp => arg.exp(),
- abs => arg.abs(),
- ceil => arg.ceil(),
- floor => arg.floor(),
- round => arg.round(),
- _ => unreachable!(),
- }
- },
- binary_expr => {
- let mut p = pair.into_inner();
-
- let op = p.next().ok_or(CalcError::ArgCount)?;
-
- let arg1 = eval_single_pair(p.next().ok_or(CalcError::ArgCount)?)?;
- let arg2 = eval_single_pair(p.next().ok_or(CalcError::ArgCount)?)?;
-
- assert!(p.next().is_none());
-
- match op.as_rule() {
- min => arg1.min(arg2),
- max => arg1.max(arg2),
- atan2 => arg1.atan2(arg2),
- _ => unreachable!(),
- }
- },
- suffix_expr => {
- let mut p = pair.into_inner();
-
- let arg = eval_expr(p.next().ok_or(CalcError::ArgCount)?.into_inner())?;
- let op = p.next().ok_or(CalcError::ArgCount)?;
-
- assert!(p.next().is_none());
-
- match op.as_rule() {
- factorial => statrs::function::gamma::gamma(arg + 1.),
- _ => unreachable!(),
- }
- },
- _ => unreachable!(),
- };
-
- Ok(result)
- }
-
- fn eval_expr(p: Pairs<Rule>) -> StdResult<f64, CalcError> {
- CLIMBER
- .map_primary(eval_single_pair)
- .map_infix(|lhs, op, rhs| {
- let lhs = lhs?;
- let rhs = rhs?;
-
- let result = match op.as_rule() {
- add => lhs + rhs,
- sub => lhs - rhs,
- mul => lhs * rhs,
- div => lhs / rhs,
- pow => lhs.powf(rhs),
- dice => {
- let dice_count = lhs as usize;
- let dice_faces = rhs as usize;
-
- let mut rng = thread_rng();
- (0..dice_count)
- .map(|_| rng.gen_range(1..(dice_faces + 1)))
- .sum::<usize>() as f64
- },
- _ => unreachable!(),
- };
-
- Ok(result)
- })
- .map_postfix(|arg, _post| arg) // discard EOI
- .parse(p)
- }
-
- eval_expr(result)
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_calc_basics() {
- assert_eq!(3., Calc::eval("1 + 2").unwrap());
- assert_eq!(3.0f64.ln(), Calc::eval("log 3").unwrap());
- assert!(6. - Calc::eval("3!").unwrap() < 0.0001);
- assert_eq!(3., Calc::eval("max 3 2").unwrap());
- }
-
- #[test]
- fn test_binary_unary() {
- assert_eq!(3.0f64.ln(), Calc::eval("max log 3 log 2").unwrap());
- }
-
- #[test]
- fn test_prefix_suffix() {
- assert!(6. - Calc::eval("abs 3!").unwrap() < 0.0001);
- }
-}
+use crate::util;
/// Roll some number of dice or perform a calculation.
#[poise::command(prefix_command, guild_only, aliases("calc", "calculate"))]
-pub async fn roll(ctx: PoiseContext<'_>, #[rest] rest: String) -> anyhow::Result<()> {
- match Calc::eval(&rest) {
+pub async fn roll<U: Send + Sync>(
+ ctx: poise::Context<'_, U, anyhow::Error>,
+ #[rest] rest: String,
+) -> anyhow::Result<()> {
+ match thulani_calc::Calc::eval(&rest) {
Ok(result) => {
- debug!("got calc result '{}'", result);
+ log::debug!("got calc result '{}'", result);
util::reply(ctx, result.to_string()).await?;
},
Err(e) => {
- error!("error encountered reading calc '{}': {}", rest, e);
+ log::error!("error encountered reading calc '{}': {}", rest, e);
util::reply(ctx, "I COULDN'T READ THAT YOU FUCK").await?;
},
}
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 0a6ec9c..74f67b1 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -4,7 +4,11 @@ use std::{
str::FromStr,
};
-use anyhow::anyhow;
+use anyhow::{
+ anyhow,
+ Error,
+ Result,
+};
use chrono::{
DateTime,
NaiveDate,
@@ -34,10 +38,6 @@ use serenity::FutureExt;
pub use self::models::*;
use self::schema::*;
-use crate::{
- Error,
- Result,
-};
mod models;
mod schema;
diff --git a/src/db/models.rs b/src/db/models.rs
index 1165368..603887e 100644
--- a/src/db/models.rs
+++ b/src/db/models.rs
@@ -1,3 +1,7 @@
+use anyhow::{
+ Error,
+ Result,
+};
use chrono::naive::NaiveDateTime;
use diesel::{
prelude::*,
@@ -11,11 +15,7 @@ use diesel_async::{
};
use sha1::Digest;
-use crate::{
- db::schema::*,
- Error,
- Result,
-};
+use crate::db::schema::*;
#[derive(Queryable, Identifiable, PartialEq, Debug, Clone)]
#[diesel(table_name = memes)]
diff --git a/src/lib.rs b/src/lib.rs
index b9009be..13a93e0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -25,8 +25,5 @@ pub use crate::{
util::*,
};
-pub type Error = anyhow::Error;
-pub type Result<T> = anyhow::Result<T>;
-
pub type PoiseData = ();
pub type PoiseContext<'a> = poise::Context<'a, PoiseData, anyhow::Error>;
diff --git a/src/log_setup.rs b/src/log_setup.rs
index 780ab78..e483c2d 100644
--- a/src/log_setup.rs
+++ b/src/log_setup.rs
@@ -1,14 +1,9 @@
-use crate::{
- Error,
- Result,
-};
-
use fern::colors::{
Color,
ColoredLevelConfig,
};
-pub fn init(file_output: bool) -> Result<()> {
+pub fn init(file_output: bool) -> anyhow::Result<()> {
let colors = ColoredLevelConfig::new()
.info(Color::Green)
.debug(Color::BrightBlue)
@@ -52,5 +47,5 @@ pub fn init(file_output: bool) -> Result<()> {
);
}
- logger.apply().map_err(Error::from)
+ logger.apply().map_err(anyhow::Error::from)
}
diff --git a/src/util/mod.rs b/src/util/mod.rs
index f362ff7..f959a37 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -32,7 +32,6 @@ use url::Url;
use crate::{
commands::playback::songbird,
PoiseContext,
- Result,
CONFIG,
};
@@ -47,7 +46,7 @@ pub async fn currently_playing(ctx: PoiseContext<'_>) -> bool {
call.queue().current().is_some()
}
-pub async fn users_listening(ctx: &Context) -> Result<bool> {
+pub async fn users_listening(ctx: &Context) -> anyhow::Result<bool> {
let channel = CONFIG.discord.voice_channel().to_channel(&ctx).await?;
let res = channel
@@ -100,12 +99,12 @@ pub fn err_msg<'a, U, E>(err: &'a FrameworkError<U, E>) -> Option<&'a Message> {
}
#[inline]
-pub fn tts(ctx: PoiseContext<'_>) -> Option<bool> {
+pub fn tts<U, E>(ctx: poise::Context<'_, U, E>) -> Option<bool> {
msg(ctx).map(|msg| msg.tts)
}
#[inline]
-pub fn unwrap_tts(ctx: PoiseContext<'_>) -> bool {
+pub fn unwrap_tts<U, E>(ctx: poise::Context<'_, U, E>) -> bool {
tts(ctx).unwrap_or(false)
}
@@ -115,12 +114,15 @@ pub async fn send(
channel: ChannelId,
text: impl AsRef<str>,
tts: bool,
-) -> Result<()> {
+) -> anyhow::Result<()> {
send_result(ctx, channel, text, tts).await.map(|_| ())
}
#[inline]
-pub async fn reply(ctx: PoiseContext<'_>, text: impl AsRef<str>) -> Result<poise::ReplyHandle> {
+pub async fn reply<U, E>(
+ ctx: poise::Context<'_, U, E>,
+ text: impl AsRef<str>,
+) -> Result<poise::ReplyHandle, serenity::Error> {
let handle =
poise::send_reply(ctx, CreateReply::default().tts(unwrap_tts(ctx)).content(text.as_ref()))
.await?;
@@ -129,7 +131,7 @@ pub async fn reply(ctx: PoiseContext<'_>, text: impl AsRef<str>) -> Result<poise
}
#[inline]
-pub async fn react(ctx: PoiseContext<'_>, react: ReactionType) -> Result<Reaction> {
+pub async fn react(ctx: PoiseContext<'_>, react: ReactionType) -> anyhow::Result<Reaction> {
let react = msg(ctx).ok_or_else(|| anyhow::anyhow!("elp"))?.react(ctx, react).await?;
Ok(react)
}
@@ -139,7 +141,7 @@ pub async fn send_result(
channel: ChannelId,
text: impl AsRef<str>,
tts: bool,
-) -> Result<MessageId> {
+) -> anyhow::Result<MessageId> {
let text = text.as_ref();
debug!("sending message {:?} to channel {:?} (tts: {})", text, channel, tts);
@@ -170,7 +172,7 @@ lazy_static! {
.unwrap();
}
-pub async fn ytdl_url(uri: &str) -> Result<String> {
+pub async fn ytdl_url(uri: &str) -> anyhow::Result<String> {
use serde_json::Value;
use tokio::process::Command;