Add fixed
This commit is contained in:
parent
11fcae4db3
commit
61fa6ee530
182
src/calc.rs
182
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<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 {
|
||||
|
@ -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
|
||||
// }
|
||||
// }
|
||||
|
13
src/main.rs
13
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<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,
|
||||
|
@ -42,7 +42,7 @@ struct App {
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> App {
|
||||
fn default() -> Self {
|
||||
App {
|
||||
input: String::new(),
|
||||
input_mode: InputMode::Normal,
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user