diff --git a/src/calc.rs b/src/calc.rs index 966e7c3..c84f08f 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -1,19 +1,19 @@ -pub mod constants; pub mod errors; pub mod operations; +pub mod types; use confy::{load, store}; -use constants::{ - CalculatorAlignment, CalculatorAngleMode, CalculatorConstant, CalculatorConstants, - CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorRegisters, CalculatorState, - RegisterState, -}; use errors::{CalculatorError, CalculatorResult}; use operations::{CalculatorOperation, CalculatorStateChange, MacroState, OpArgs}; use serde::ser::Serializer; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; use std::collections::{HashSet, VecDeque}; +use types::{ + CalculatorAlignment, CalculatorAngleMode, CalculatorConstant, CalculatorConstants, + CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorRegisters, CalculatorState, + RegisterState, +}; const MAX_PRECISION: usize = 20; const APP_NAME: &str = "rpn_rs"; @@ -66,7 +66,7 @@ where impl Default for Calculator { fn default() -> Self { - Calculator { + Self { l: String::new(), redo_buf: vec![], undo_buf: vec![], @@ -136,13 +136,15 @@ impl Calculator { } } - pub fn load_config() -> CalculatorResult { + pub fn load_config() -> CalculatorResult { load(APP_NAME).map_err(|e| CalculatorError::LoadError(Some(e))) } pub fn save_config(&self) -> CalculatorResult<()> { store(APP_NAME, self).map_err(|e| CalculatorError::SaveError(Some(e))) } + // This maps chars to operations. Not sure I can make this shorter + #[allow(clippy::too_many_lines)] pub fn take_input(&mut self, c: char) -> CalculatorResult<()> { match &self.state { CalculatorState::Normal => match c { @@ -274,12 +276,8 @@ impl Calculator { } } 'S' => { - let precision = self.checked_get(0)? as usize; - if precision > MAX_PRECISION { - return Err(CalculatorError::PrecisionTooHigh); - } self.display_mode = CalculatorDisplayMode::Scientific { - precision: self.pop_usize()?, + precision: self.pop_precision()?, } } 'e' => { @@ -288,12 +286,8 @@ impl Calculator { } } 'E' => { - let precision = self.checked_get(0)? as usize; - if precision > MAX_PRECISION { - return Err(CalculatorError::PrecisionTooHigh); - } self.display_mode = CalculatorDisplayMode::Engineering { - precision: self.pop_usize()?, + precision: self.pop_precision()?, } } 'f' => { @@ -303,7 +297,7 @@ impl Calculator { } 'F' => { self.display_mode = CalculatorDisplayMode::Fixed { - precision: self.pop_usize()?, + precision: self.pop_precision()?, } } 'w' => self.save_on_close = false, @@ -405,24 +399,36 @@ impl Calculator { push: OpArgs::Unary(f), }) } - pub fn pop(&mut self) -> CalculatorResult { + pub fn peek(&mut self) -> CalculatorResult { self.flush_l()?; - let f = self.checked_get(0)?; + self.checked_get(0) + } + pub fn pop(&mut self) -> CalculatorResult { + let f = self.peek()?; self.direct_state_change(CalculatorStateChange { pop: OpArgs::Unary(f), push: OpArgs::None, })?; Ok(f) } - pub fn pop_usize(&mut self) -> CalculatorResult { - self.flush_l()?; - let f = self.checked_get(0)?; - let ret = f as usize; + pub fn pop_precision(&mut self) -> CalculatorResult { + let f = self.peek()?; + // Ensure this can be cast to a usize + if !f.is_finite() || f.is_sign_negative() { + return Err(CalculatorError::ArithmeticError); + } + #[allow(clippy::cast_sign_loss)] + let u = f as usize; + + if u > MAX_PRECISION { + return Err(CalculatorError::PrecisionTooHigh); + } + self.direct_state_change(CalculatorStateChange { pop: OpArgs::Unary(f), push: OpArgs::None, })?; - Ok(ret) + Ok(u) } pub fn op(&mut self, op: CalculatorOperation) -> CalculatorResult<()> { // Dup is special -- don't actually run it if l needs to be flushed @@ -441,7 +447,7 @@ impl Calculator { } CalculatorOperation::Negate => self.unary_op(|a| OpArgs::Unary(-a)), CalculatorOperation::AbsoluteValue => self.unary_op(|a| OpArgs::Unary(a.abs())), - CalculatorOperation::Inverse => self.unary_op(|a| OpArgs::Unary(1.0 / a)), + CalculatorOperation::Inverse => self.unary_op(|a| OpArgs::Unary(a.recip())), CalculatorOperation::Modulo => self.binary_op(|[a, b]| OpArgs::Unary(b % a)), //CalculatorOperation::Remainder => self.binary_op(|[a, b]| OpArgs::Unary(b.rem_euclid(a))), CalculatorOperation::Dup => self.unary_op(|a| OpArgs::Binary([a, a])), @@ -493,7 +499,7 @@ impl Calculator { CalculatorOperation::Log => self.unary_op(|a| OpArgs::Unary(a.log10())), CalculatorOperation::Ln => self.unary_op(|a| OpArgs::Unary(a.ln())), CalculatorOperation::Pow => self.binary_op(|[a, b]| OpArgs::Unary(b.powf(a))), - CalculatorOperation::E => self.binary_op(|[a, b]| OpArgs::Unary(b * 10.0f64.powf(a))), + CalculatorOperation::E => self.binary_op(|[a, b]| OpArgs::Unary(b * 10.0_f64.powf(a))), CalculatorOperation::Undo => return self.history_op(false), CalculatorOperation::Redo => return self.history_op(true), // Macros are a no-op operator; need to insert for undo/redo diff --git a/src/calc/errors.rs b/src/calc/errors.rs index 168058e..5bb0a42 100644 --- a/src/calc/errors.rs +++ b/src/calc/errors.rs @@ -27,30 +27,30 @@ impl error::Error for CalculatorError {} impl fmt::Display for CalculatorError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - CalculatorError::ArithmeticError => write!(f, "Arithmetic Error"), - CalculatorError::NotEnoughStackEntries => write!(f, "Not enough items in the stack"), - CalculatorError::CorruptStateChange(msg) => { + Self::ArithmeticError => write!(f, "Arithmetic Error"), + Self::NotEnoughStackEntries => write!(f, "Not enough items in the stack"), + Self::CorruptStateChange(msg) => { write!(f, "Corrupt state change: {}", msg) } - CalculatorError::EmptyHistory(msg) => write!(f, "No history to {}", msg), - CalculatorError::NoSuchOperator(c) => write!(f, "No such operator '{}'", c), - CalculatorError::NoSuchConstant(c) => write!(f, "No such constant '{}'", c), - CalculatorError::NoSuchRegister(c) => write!(f, "No such register '{}'", c), - CalculatorError::NoSuchMacro(c) => write!(f, "No such macro '{}'", c), - CalculatorError::NoSuchSetting(c) => write!(f, "No such setting '{}'", c), - CalculatorError::RecursiveMacro(c) => write!(f, "Recursive macro '{}'", c), - CalculatorError::ParseError => write!(f, "Parse error"), - CalculatorError::PrecisionTooHigh => write!(f, "Precision too high"), - CalculatorError::SaveError(None) => write!(f, "Could not save"), - CalculatorError::SaveError(Some(ConfyError::SerializeTomlError(e))) => { + Self::EmptyHistory(msg) => write!(f, "No history to {}", msg), + Self::NoSuchOperator(c) => write!(f, "No such operator '{}'", c), + Self::NoSuchConstant(c) => write!(f, "No such constant '{}'", c), + Self::NoSuchRegister(c) => write!(f, "No such register '{}'", c), + Self::NoSuchMacro(c) => write!(f, "No such macro '{}'", c), + Self::NoSuchSetting(c) => write!(f, "No such setting '{}'", c), + Self::RecursiveMacro(c) => write!(f, "Recursive macro '{}'", c), + Self::ParseError => write!(f, "Parse error"), + Self::PrecisionTooHigh => write!(f, "Precision too high"), + Self::SaveError(None) => write!(f, "Could not save"), + Self::SaveError(Some(ConfyError::SerializeTomlError(e))) => { write!(f, "Save serialization error: {}", e) } - CalculatorError::SaveError(Some(e)) => write!(f, "Could not save: {}", e), - CalculatorError::LoadError(None) => write!(f, "Could not load"), - CalculatorError::LoadError(Some(ConfyError::SerializeTomlError(e))) => { + Self::SaveError(Some(e)) => write!(f, "Could not save: {}", e), + Self::LoadError(None) => write!(f, "Could not load"), + Self::LoadError(Some(ConfyError::SerializeTomlError(e))) => { write!(f, "Load serialization error: {}", e) } - CalculatorError::LoadError(Some(e)) => write!(f, "Could not load: {}", e), + Self::LoadError(Some(e)) => write!(f, "Could not load: {}", e), } } } diff --git a/src/calc/constants.rs b/src/calc/types.rs similarity index 70% rename from src/calc/constants.rs rename to src/calc/types.rs index 5ff5275..7aae9a4 100644 --- a/src/calc/constants.rs +++ b/src/calc/types.rs @@ -19,7 +19,7 @@ pub enum CalculatorState { impl Default for CalculatorState { fn default() -> Self { - CalculatorState::Normal + Self::Normal } } @@ -53,16 +53,16 @@ pub enum CalculatorAngleMode { impl Default for CalculatorAngleMode { fn default() -> Self { - CalculatorAngleMode::Degrees + Self::Degrees } } impl fmt::Display for CalculatorAngleMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - CalculatorAngleMode::Degrees => write!(f, "DEG"), - CalculatorAngleMode::Radians => write!(f, "RAD"), - CalculatorAngleMode::Grads => write!(f, "GRD"), + Self::Degrees => write!(f, "DEG"), + Self::Radians => write!(f, "RAD"), + Self::Grads => write!(f, "GRD"), } } } @@ -81,18 +81,18 @@ pub enum CalculatorDisplayMode { impl fmt::Display for CalculatorDisplayMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - CalculatorDisplayMode::Default => write!(f, "DEF"), - CalculatorDisplayMode::Separated { separator } => write!(f, "SEP({})", separator), - CalculatorDisplayMode::Scientific { precision } => write!(f, "SCI({})", precision), - CalculatorDisplayMode::Engineering { precision } => write!(f, "ENG({})", precision), - CalculatorDisplayMode::Fixed { precision } => write!(f, "FIX({})", precision), + Self::Default => write!(f, "DEF"), + Self::Separated { separator } => write!(f, "SEP({})", separator), + Self::Scientific { precision } => write!(f, "SCI({})", precision), + Self::Engineering { precision } => write!(f, "ENG({})", precision), + Self::Fixed { precision } => write!(f, "FIX({})", precision), } } } impl Default for CalculatorDisplayMode { fn default() -> Self { - CalculatorDisplayMode::Default + Self::Default } } @@ -104,15 +104,15 @@ pub enum CalculatorAlignment { impl Default for CalculatorAlignment { fn default() -> Self { - CalculatorAlignment::Left + Self::Left } } impl fmt::Display for CalculatorAlignment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - CalculatorAlignment::Left => write!(f, "L"), - CalculatorAlignment::Right => write!(f, "R"), + Self::Left => write!(f, "L"), + Self::Right => write!(f, "R"), } } } diff --git a/src/event.rs b/src/event.rs index 27f6ce9..af22b1b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -19,7 +19,7 @@ pub struct Events { } impl Events { - pub fn new(tick_rate: u64) -> Events { + pub fn new(tick_rate: u64) -> Self { let (tx, rx) = mpsc::channel(); let tick_handle = { let tx = tx.clone(); @@ -44,14 +44,14 @@ impl Events { } }) }; - Events { + Self { tx, rx, tick_handle, } } - /// Returns a chain of the next element (blocking) and a TryIter of everything after it. + /// Returns a chain of the next element (blocking) and a `TryIter` of everything after it. /// Useful for reading a single Iterator of all elements once after blocking, so there are not too many UI redraws. pub fn blocking_iter( &self, diff --git a/src/format.rs b/src/format.rs index a24c86a..7849118 100644 --- a/src/format.rs +++ b/src/format.rs @@ -2,13 +2,11 @@ pub fn scientific(f: f64, precision: usize) -> String { let mut ret = format!("{:.precision$E}", f, precision = precision); let exp = ret.split_off(ret.find('E').unwrap_or(0)); - let (exp_sign, exp) = if let Some(stripped) = exp.strip_prefix("E-") { - ('-', stripped) - } else { - ('+', &exp[1..]) - }; + let (exp_sign, exp) = exp + .strip_prefix("E-") + .map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped)); - let sign = if !ret.starts_with('-') { " " } else { "" }; + let sign = if ret.starts_with('-') { "" } else { " " }; format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2) } @@ -48,9 +46,9 @@ pub fn engineering(f: f64, precision: usize) -> String { // Whole portion of number. Slice is safe because the num_whole_digits is always 3 and the num_str will always have length >= 3 since precision in all=2 (+original whole digit) // Original number is 1,000 => whole will be 1, if original is 0.01, whole will be 10 - let whole = &num_str[0..(num_whole_digits + 1)]; + let whole = &num_str[0..=num_whole_digits]; // Decimal portion of the number. Sliced from the number of whole digits to the *requested* precision. Precision generated in all will be requested precision + 2 - let decimal = &num_str[(num_whole_digits + 1)..(precision + num_whole_digits + 1)]; + let decimal = &num_str[(num_whole_digits + 1)..=(precision + num_whole_digits)]; // Right align whole portion, always have decimal point format!( "{: >4}.{} E{}{:0>pad$}", diff --git a/src/main.rs b/src/main.rs index b20cfa6..ce80e02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,16 @@ +#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] +// This is intended behavior +#![allow(clippy::cast_possible_truncation)] +// Cannot fix this, so don't warn me about it +#![allow(clippy::multiple_crate_versions)] + mod calc; mod event; mod format; use calc::{ - constants::{CalculatorAlignment, CalculatorDisplayMode, CalculatorState, RegisterState}, errors::CalculatorResult, + types::{CalculatorAlignment, CalculatorDisplayMode, CalculatorState, RegisterState}, Calculator, }; use crossterm::{ @@ -53,7 +59,7 @@ impl Default for App { Ok(c) => (c, None), Err(e) => (Calculator::default(), Some(format!("{}", e))), }; - App { + Self { calculator, error_msg, state: AppState::Calculator, @@ -61,11 +67,13 @@ impl Default for App { } } impl App { - fn draw_clippy_dialogs(&mut self, f: &mut Frame>) { + // This function is long because it contains help text + #[allow(clippy::too_many_lines)] + fn draw_clippy_dialogs(&mut self, f: &mut Frame>) { match (&self.state, &self.calculator.state) { (AppState::Help, _) => { draw_clippy_rect( - ClippyRectangle { + &ClippyRectangle { title: "Help", msg: "\ + => Add s => Sin\n\ @@ -92,7 +100,7 @@ impl App { } (AppState::Calculator, CalculatorState::WaitingForConstant) => { draw_clippy_rect( - ClippyRectangle { + &ClippyRectangle { title: "Constants", msg: self .calculator @@ -113,7 +121,7 @@ impl App { RegisterState::Load => "Registers", }; draw_clippy_rect( - ClippyRectangle { + &ClippyRectangle { title, msg: self .calculator @@ -128,7 +136,7 @@ impl App { } (AppState::Calculator, CalculatorState::WaitingForMacro) => { draw_clippy_rect( - ClippyRectangle { + &ClippyRectangle { title: "Macros", msg: self .calculator @@ -143,7 +151,7 @@ impl App { } (AppState::Calculator, CalculatorState::WaitingForSetting) => { draw_clippy_rect( - ClippyRectangle { + &ClippyRectangle { title: "Settings", msg: "\ d => Degrees\n\ @@ -170,7 +178,7 @@ impl App { } } - pub fn terminal_draw_func(&mut self, f: &mut Frame>) { + pub fn terminal_draw_func(&mut self, f: &mut Frame>) { let chunks = Layout::default() .direction(Direction::Vertical) .margin(2) @@ -304,7 +312,7 @@ fn main() -> Result<(), Box> { Err(e) => Some(format!("{}", e)), }; } - _ => continue, + Event::Tick => continue, } } } @@ -318,6 +326,8 @@ fn main() -> Result<(), Box> { app.calculator.close().map_err(|e| e.into()) } +// This function is long since it maps keys to their proper value +#[allow(clippy::too_many_lines)] fn handle_key(app: &mut App, key: KeyEvent) -> CalculatorResult { match (&app.state, &app.calculator.state) { (AppState::Calculator, CalculatorState::Normal) => match key { @@ -452,7 +462,7 @@ impl ClippyRectangle<'_> { } } -fn draw_clippy_rect(c: ClippyRectangle, f: &mut Frame>) { +fn draw_clippy_rect(c: &ClippyRectangle, f: &mut Frame>) { // let block = Block::default().title(c.title).borders(Borders::ALL); let dimensions = c.size(); let popup_layout = Layout::default()