diff options
| author | Nathan Perry <np@nathanperry.dev> | 2024-08-07 07:39:16 -0400 |
|---|---|---|
| committer | Nathan Perry <np@nathanperry.dev> | 2024-08-07 07:39:16 -0400 |
| commit | b58b03c22d637fe8f7200edb6953325bf359544d (patch) | |
| tree | 606865b7afc4498b02da14895723b2b0ed49760e /src | |
| parent | 96c197bde0f83d8b99ec66238856c76b41bfd5e1 (diff) | |
split calc into separate subcrate
Diffstat (limited to 'src')
| -rw-r--r-- | src/bot.rs | 10 | ||||
| -rw-r--r-- | src/commands/calc.pest | 79 | ||||
| -rw-r--r-- | src/commands/game.rs | 5 | ||||
| -rw-r--r-- | src/commands/roll.rs | 212 | ||||
| -rw-r--r-- | src/db/mod.rs | 10 | ||||
| -rw-r--r-- | src/db/models.rs | 10 | ||||
| -rw-r--r-- | src/lib.rs | 3 | ||||
| -rw-r--r-- | src/log_setup.rs | 9 | ||||
| -rw-r--r-- | src/util/mod.rs | 20 |
9 files changed, 37 insertions, 321 deletions
@@ -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)] @@ -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; |
