Add fixed

This commit is contained in:
Austen Adler 2021-05-05 00:10:58 -04:00
parent 11fcae4db3
commit 61fa6ee530
5 changed files with 123 additions and 213 deletions

View File

@ -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<char>,
#[serde(serialize_with = "ordered_char_map")]
constants: CalculatorConstants,
#[serde(skip)]
registers: CalculatorRegisters,
#[serde(skip)]
undo_buf: Vec<CalculatorStateChange>,
@ -46,12 +50,9 @@ pub struct Calculator {
redo_buf: Vec<CalculatorStateChange>,
#[serde(skip)]
state: CalculatorState,
// TODO:
#[serde(skip)]
angle_mode: CalculatorAngleMode,
// TODO:
#[serde(skip)]
display_mode: CalculatorDisplayMode,
// calculator_alignment: CalculatorAlignment,
}
fn ordered_char_map<S, T>(value: &HashMap<char, T>, serializer: S) -> Result<S::Ok, S::Error>
@ -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.")
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("<cr>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 Serialize for Calculator {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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()
}
}
impl Calculator {
pub fn with_settings(settings: CalculatorSettings) -> CalculatorResult<Calculator> {
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::<CalculatorResult<CalculatorMacros>>()?,
constants: settings
.constants
.into_iter()
.map(|(key, constant)| {
Ok((
key.chars()
.next()
.ok_or_else(|| CalculatorError::LoadError(None))?,
constant,
))
})
.collect::<CalculatorResult<CalculatorConstants>>()?,
angle_mode,
display_mode,
})
}
pub fn load_config() -> CalculatorResult<Calculator> {
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 {

View File

@ -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<char>),
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<f64>,
#[serde(skip)]
pub display_mode: CalculatorDisplayMode,
pub angle_mode: CalculatorAngleMode,
pub macros: HashMap<String, CalculatorMacro>,
pub constants: HashMap<String, CalculatorConstant>,
}
// #[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("<cr>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
// }
// }

View File

@ -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<dyn Error>> {
.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<dyn Error>> {
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,

View File

@ -42,7 +42,7 @@ struct App {
}
impl Default for App {
fn default() -> App {
fn default() -> Self {
App {
input: String::new(),
input_mode: InputMode::Normal,

View File

@ -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),