diff --git a/src/calc.rs b/src/calc.rs index 2e7e931..202e678 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -4,18 +4,19 @@ pub mod operations; use confy::{load, store}; use constants::{ - CalculatorAngleMode, CalculatorConstants, CalculatorConstantsIter, CalculatorDisplayMode, - CalculatorMacros, CalculatorMacrosIter, CalculatorRegisters, CalculatorRegistersIter, - CalculatorSettings, CalculatorState, RegisterState, + CalculatorAngleMode, CalculatorConstant, CalculatorConstants, CalculatorConstantsIter, + CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorMacrosIter, + CalculatorRegisters, CalculatorRegistersIter, CalculatorState, RegisterState, }; use errors::{CalculatorError, CalculatorResult}; use operations::{CalculatorOperation, CalculatorStateChange, MacroState, OpArgs}; -use serde::ser::{SerializeStruct, Serializer}; +use serde::ser::Serializer; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; use std::collections::{HashSet, VecDeque}; const APP_NAME: &str = "rpn_rs"; +const DEFAULT_PRECISION: usize = 3; #[derive(PartialEq, Debug, Serialize, Deserialize)] enum HistoryMode { @@ -29,7 +30,8 @@ impl CalculatorStateChange { } } -#[derive(Deserialize)] +#[derive(Serialize, Deserialize)] +#[serde(default)] pub struct Calculator { #[serde(skip)] l: String, @@ -38,7 +40,9 @@ pub struct Calculator { macros: CalculatorMacros, #[serde(skip)] active_macros: HashSet, + #[serde(serialize_with = "ordered_char_map")] constants: CalculatorConstants, + #[serde(skip)] registers: CalculatorRegisters, #[serde(skip)] undo_buf: Vec, @@ -46,12 +50,9 @@ pub struct Calculator { redo_buf: Vec, #[serde(skip)] state: CalculatorState, - // TODO: - #[serde(skip)] angle_mode: CalculatorAngleMode, - // TODO: - #[serde(skip)] display_mode: CalculatorDisplayMode, + // calculator_alignment: CalculatorAlignment, } fn ordered_char_map(value: &HashMap, serializer: S) -> Result @@ -59,10 +60,7 @@ where T: Serialize, S: Serializer, { - // let ordered: HashMap<_, _> = value - // .iter() - // .map(|(key, t)| (String::from(*key), t.clone())) - // .collect(); + // Convert chars to string for TOML map; insert into BTreeMap to be sorted let ordered: BTreeMap<_, _> = value .iter() .map(|(key, t)| (String::from(*key), t.clone())) @@ -72,94 +70,87 @@ where impl Default for Calculator { fn default() -> Self { - Calculator::with_settings(CalculatorSettings::default()) - .expect("Default calculator config is corrupt! Cannot start.") - } -} - -impl Serialize for Calculator { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("Calculator", 2)?; - s.serialize_field("l", &self.l)?; - s.serialize_field("state", &self.state)?; - // s.serialize_field("k", &self.registers)?; // map key was not a string - //s.serialize_field("t", &self.undo_buf)?; // unsupported Rust type - s.end() + Calculator { + l: String::new(), + redo_buf: vec![], + undo_buf: vec![], + active_macros: HashSet::new(), + registers: CalculatorRegisters::new(), + state: CalculatorState::Normal, + stack: vec![1.0, 2.0].into_iter().collect(), + macros: [ + ( + 'e', + CalculatorMacro { + help: String::from("Empty"), + value: String::from(""), + }, + ), + ( + 'm', + CalculatorMacro { + help: String::from("64?>64%"), + value: String::from(" 64?>64%"), + }, + ), + ( + 'u', + CalculatorMacro { + help: String::from("Quadratic Formula"), + value: String::from( + "RcRbRarbnrb2 ^4 rarc**-v+2 ra*/rbnrb2^4 rarc**-v-2 ra*/", + ), + }, + ), + ] + .iter() + .cloned() + .collect(), + constants: [ + ( + 't', + CalculatorConstant { + help: String::from("Tau (2pi)"), + value: std::f64::consts::TAU, + }, + ), + ( + 'e', + CalculatorConstant { + help: String::from("Euler's Number e"), + value: std::f64::consts::E, + }, + ), + ( + 'p', + CalculatorConstant { + help: String::from("Pi"), + value: std::f64::consts::PI, + }, + ), + ] + .iter() + .cloned() + .collect(), + angle_mode: CalculatorAngleMode::default(), + display_mode: CalculatorDisplayMode::default(), + // calculator_alignment: CalculatorAlignment::default(), + } } } impl Calculator { - pub fn with_settings(settings: CalculatorSettings) -> CalculatorResult { - let angle_mode = settings.angle_mode; - let display_mode = settings.display_mode; - Ok(Calculator { - l: String::new(), - state: CalculatorState::Normal, - undo_buf: vec![], - redo_buf: vec![], - active_macros: HashSet::new(), - registers: CalculatorRegisters::new(), - stack: settings.stack.into(), - macros: settings - .macros - .into_iter() - .map(|(key, mac)| { - Ok(( - key.chars() - .next() - .ok_or_else(|| CalculatorError::LoadError(None))?, - mac, - )) - }) - .collect::>()?, - constants: settings - .constants - .into_iter() - .map(|(key, constant)| { - Ok(( - key.chars() - .next() - .ok_or_else(|| CalculatorError::LoadError(None))?, - constant, - )) - }) - .collect::>()?, - angle_mode, - display_mode, - }) - } - pub fn load_config() -> CalculatorResult { - let settings = load(APP_NAME).map_err(|e| CalculatorError::LoadError(Some(e)))?; - Calculator::with_settings(settings) + load(APP_NAME).map_err(|e| CalculatorError::LoadError(Some(e))) } pub fn save_config(&self) -> CalculatorResult<()> { - let settings = CalculatorSettings { - angle_mode: self.angle_mode.clone(), - display_mode: self.display_mode.clone(), - stack: self.stack.iter().map(|f| *f).collect(), - macros: self - .macros - .iter() - .map(|(key, mac)| (String::from(*key), mac.clone())) - .collect(), - constants: self - .constants - .iter() - .map(|(key, constant)| (String::from(*key), constant.clone())) - .collect(), - }; - store(APP_NAME, settings).map_err(|e| CalculatorError::SaveError(Some(e))) + store(APP_NAME, self).map_err(|e| CalculatorError::SaveError(Some(e))) } pub fn take_input(&mut self, c: char) -> CalculatorResult<()> { match &self.state { CalculatorState::Normal => match c { c @ '0'..='9' | c @ '.' | c @ 'e' => self.entry(c), - //'!' => self.op(CalculatorOperation::Factorial), '+' => self.op(CalculatorOperation::Add), '-' => self.op(CalculatorOperation::Subtract), '*' => self.op(CalculatorOperation::Multiply), @@ -182,7 +173,6 @@ impl Calculator { '^' => self.op(CalculatorOperation::Pow), 'l' => self.op(CalculatorOperation::Log), 'L' => self.op(CalculatorOperation::Ln), - //'e' => self.op(CalculatorOperation::E), // Special 'u' => self.op(CalculatorOperation::Undo), 'U' => self.op(CalculatorOperation::Redo), @@ -277,10 +267,10 @@ impl Calculator { 'd' => self.angle_mode = CalculatorAngleMode::Degrees, 'r' => self.angle_mode = CalculatorAngleMode::Radians, 'g' => self.angle_mode = CalculatorAngleMode::Grads, - '_' => self.display_mode = CalculatorDisplayMode::Default(None), - ',' => self.display_mode = CalculatorDisplayMode::Default(Some(',')), - ' ' => self.display_mode = CalculatorDisplayMode::Default(Some(' ')), - 's' => self.display_mode = CalculatorDisplayMode::Scientific(3), + '_' => self.display_mode = CalculatorDisplayMode::Default, + ',' => self.display_mode = CalculatorDisplayMode::Separated(','), + ' ' => self.display_mode = CalculatorDisplayMode::Separated(' '), + 's' => self.display_mode = CalculatorDisplayMode::Scientific(DEFAULT_PRECISION), 'S' => { let precision = self.checked_get(0)? as usize; if precision >= 20 { @@ -288,7 +278,9 @@ impl Calculator { } self.display_mode = CalculatorDisplayMode::Scientific(self.pop_usize()?) } - 'e' => self.display_mode = CalculatorDisplayMode::Engineering(3), + 'e' => { + self.display_mode = CalculatorDisplayMode::Engineering(DEFAULT_PRECISION) + } 'E' => { let precision = self.checked_get(0)? as usize; if precision >= 20 { @@ -296,6 +288,8 @@ impl Calculator { } self.display_mode = CalculatorDisplayMode::Engineering(self.pop_usize()?) } + 'f' => self.display_mode = CalculatorDisplayMode::Fixed(DEFAULT_PRECISION), + 'F' => self.display_mode = CalculatorDisplayMode::Fixed(self.pop_usize()?), _ => return Err(CalculatorError::NoSuchSetting(c)), }; self.state = CalculatorState::Normal; @@ -486,7 +480,6 @@ impl Calculator { } }), CalculatorOperation::Sqrt => self.unary_op(|a| OpArgs::Unary(a.sqrt())), - // CalculatorOperation::Factorial => self.unary_op(|a| OpArgs::Unary(a.())), 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))), @@ -555,7 +548,6 @@ impl Calculator { } else { MacroState::Start }); - // println!("{:?} {:?}", s, history_mode); self.apply_state_change(s, forward)?; if history_mode == HistoryMode::One || macro_end { diff --git a/src/calc/constants.rs b/src/calc/constants.rs index 8d1f260..e7a4e99 100644 --- a/src/calc/constants.rs +++ b/src/calc/constants.rs @@ -55,6 +55,12 @@ pub enum CalculatorAngleMode { Grads, } +impl Default for CalculatorAngleMode { + fn default() -> Self { + CalculatorAngleMode::Degrees + } +} + impl fmt::Display for CalculatorAngleMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -65,136 +71,43 @@ impl fmt::Display for CalculatorAngleMode { } } -impl Default for CalculatorAngleMode { - fn default() -> Self { - CalculatorAngleMode::Degrees - } -} - #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(tag = "display_mode")] pub enum CalculatorDisplayMode { - Default(Option), + Default, + Separated(char), Scientific(usize), Engineering(usize), + Fixed(usize), } impl fmt::Display for CalculatorDisplayMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - CalculatorDisplayMode::Default(None) => write!(f, "DEF"), - CalculatorDisplayMode::Default(Some(c)) => write!(f, "DEF({})", c), + CalculatorDisplayMode::Default => write!(f, "DEF"), + CalculatorDisplayMode::Separated(c) => write!(f, "DEF({})", c), CalculatorDisplayMode::Scientific(p) => write!(f, "SCI({})", p), CalculatorDisplayMode::Engineering(p) => write!(f, "ENG({})", p), + CalculatorDisplayMode::Fixed(p) => write!(f, "FIX({})", p), } } } impl Default for CalculatorDisplayMode { fn default() -> Self { - CalculatorDisplayMode::Default(None) + CalculatorDisplayMode::Default } } -#[derive(Debug, Serialize, Deserialize)] -pub struct CalculatorSettings { - // Order matters here as macros/constants cannot come first - pub stack: Vec, - #[serde(skip)] - pub display_mode: CalculatorDisplayMode, - pub angle_mode: CalculatorAngleMode, - pub macros: HashMap, - pub constants: HashMap, -} +// #[derive(Clone, Debug, Serialize, Deserialize)] +// #[serde(tag = "calculator_alignment")] +// pub enum CalculatorAlignment { +// Right, +// Left, +// } -impl Default for CalculatorSettings { - fn default() -> CalculatorSettings { - CalculatorSettings { - stack: vec![1.0, 2.0].into_iter().collect(), - macros: [ - ( - String::from('e'), - CalculatorMacro { - help: String::from("Empty"), - value: String::from(""), - }, - ), - ( - String::from('1'), - CalculatorMacro { - help: String::from("Push 1"), - value: String::from("1 "), - }, - ), - ( - String::from('0'), - CalculatorMacro { - help: String::from("Push 0"), - value: String::from("0 "), - }, - ), - ( - String::from('t'), - CalculatorMacro { - help: String::from("Test"), - value: String::from("m1m0\\m1m1\\\\"), - }, - ), - ( - String::from('m'), - CalculatorMacro { - help: String::from("64?>64%"), - value: String::from(" 64?>64%"), - }, - ), - ( - String::from('u'), - CalculatorMacro { - help: String::from("Quadratic Formula"), - value: String::from( - "RcRbRarbnrb2 ^4 rarc**-v+2 ra*/rbnrb2^4 rarc**-v-2 ra*/", - ), - }, - ), - ( - String::from('s'), - CalculatorMacro { - help: String::from("Sample data"), - value: String::from("\\\\2 5 3n"), - }, - ), - ] - .iter() - .cloned() - .collect(), - constants: [ - ( - String::from('t'), - CalculatorConstant { - help: String::from("Tau (2pi)"), - value: std::f64::consts::TAU, - }, - ), - ( - String::from('e'), - CalculatorConstant { - help: String::from("Euler's Number e"), - value: std::f64::consts::E, - }, - ), - ( - String::from('p'), - CalculatorConstant { - help: String::from("Pi"), - value: std::f64::consts::PI, - }, - ), - ] - .iter() - .cloned() - .collect(), - angle_mode: CalculatorAngleMode::Degrees, - display_mode: CalculatorDisplayMode::Default(None), - } - } -} +// impl Default for CalculatorAlignment { +// fn default() -> Self { +// CalculatorAlignment::Left +// } +// } diff --git a/src/main.rs b/src/main.rs index 24c6017..8c46457 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,7 +40,7 @@ struct App { } impl Default for App { - fn default() -> App { + fn default() -> Self { let (calculator, error_msg) = match Calculator::load_config() { Ok(c) => (c, None), Err(e) => (Calculator::default(), Some(format!("{}", e))), @@ -118,14 +118,17 @@ fn main() -> Result<(), Box> { .map(|(i, m)| { let content = vec![Spans::from(Span::raw( match app.calculator.get_display_mode() { - CalculatorDisplayMode::Default(None) => format!("{:>2}: {}", i, *m), - CalculatorDisplayMode::Default(Some(c)) => fmt_separated(i, *m, *c), + CalculatorDisplayMode::Default => format!("{:>2}: {}", i, *m), + CalculatorDisplayMode::Separated(c) => fmt_separated(i, *m, *c), CalculatorDisplayMode::Scientific(precision) => { fmt_scientific(i, *m, *precision) } CalculatorDisplayMode::Engineering(_precision) => { format!("{:>2}: {}", i, m) } + CalculatorDisplayMode::Fixed(precision) => { + format!("{:>2}: {:.precision$}", i, m, precision = precision) + } }, ))]; ListItem::new(content) @@ -240,7 +243,9 @@ fn main() -> Result<(), Box> { s => Scientific\n\ S => Scientific (stack precision)\n\ e => Engineering\n\ - E => Engineering (stack precision)\ + E => Engineering (stack precision)\n\ + f => Engineering\n\ + F => Engineering (stack precision)\ ", }, f, diff --git a/src/user_input.rs b/src/user_input.rs index 6fcb631..83c74bc 100644 --- a/src/user_input.rs +++ b/src/user_input.rs @@ -42,7 +42,7 @@ struct App { } impl Default for App { - fn default() -> App { + fn default() -> Self { App { input: String::new(), input_mode: InputMode::Normal, diff --git a/src/util/event.rs b/src/util/event.rs index 5d284cf..7accf73 100644 --- a/src/util/event.rs +++ b/src/util/event.rs @@ -35,7 +35,7 @@ pub struct Config { } impl Default for Config { - fn default() -> Config { + fn default() -> Self { Config { exit_key: Key::Char('q'), tick_rate: Duration::from_millis(250),