Start serializing
This commit is contained in:
parent
13d4464ea2
commit
11fcae4db3
206
src/calc.rs
206
src/calc.rs
@ -2,16 +2,21 @@ pub mod constants;
|
||||
pub mod errors;
|
||||
pub mod operations;
|
||||
|
||||
use confy::{load, store};
|
||||
use constants::{
|
||||
CalculatorAngleMode, CalculatorConstant, CalculatorConstants, CalculatorConstantsIter,
|
||||
CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorMacrosIter,
|
||||
CalculatorRegisters, CalculatorRegistersIter, CalculatorState, RegisterState,
|
||||
CalculatorAngleMode, CalculatorConstants, CalculatorConstantsIter, CalculatorDisplayMode,
|
||||
CalculatorMacros, CalculatorMacrosIter, CalculatorRegisters, CalculatorRegistersIter,
|
||||
CalculatorSettings, CalculatorState, RegisterState,
|
||||
};
|
||||
use errors::{CalculatorError, CalculatorResult};
|
||||
use operations::{CalculatorOperation, CalculatorStateChange, MacroState, OpArgs};
|
||||
use serde::ser::{SerializeStruct, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
|
||||
const APP_NAME: &str = "rpn_rs";
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
enum HistoryMode {
|
||||
One,
|
||||
@ -24,122 +29,133 @@ impl CalculatorStateChange {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Deserialize)]
|
||||
pub struct Calculator {
|
||||
#[serde(skip)]
|
||||
l: String,
|
||||
stack: VecDeque<f64>,
|
||||
#[serde(serialize_with = "ordered_char_map")]
|
||||
macros: CalculatorMacros,
|
||||
#[serde(skip)]
|
||||
active_macros: HashSet<char>,
|
||||
constants: CalculatorConstants,
|
||||
registers: CalculatorRegisters,
|
||||
#[serde(skip)]
|
||||
undo_buf: Vec<CalculatorStateChange>,
|
||||
#[serde(skip)]
|
||||
redo_buf: Vec<CalculatorStateChange>,
|
||||
#[serde(skip)]
|
||||
state: CalculatorState,
|
||||
// TODO:
|
||||
#[serde(skip)]
|
||||
angle_mode: CalculatorAngleMode,
|
||||
// TODO:
|
||||
#[serde(skip)]
|
||||
display_mode: CalculatorDisplayMode,
|
||||
}
|
||||
|
||||
fn ordered_char_map<S, T>(value: &HashMap<char, T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
S: Serializer,
|
||||
{
|
||||
// let ordered: HashMap<_, _> = value
|
||||
// .iter()
|
||||
// .map(|(key, t)| (String::from(*key), t.clone()))
|
||||
// .collect();
|
||||
let ordered: BTreeMap<_, _> = value
|
||||
.iter()
|
||||
.map(|(key, t)| (String::from(*key), t.clone()))
|
||||
.collect();
|
||||
ordered.serialize(serializer)
|
||||
}
|
||||
|
||||
impl Default for Calculator {
|
||||
fn default() -> Calculator {
|
||||
Calculator {
|
||||
fn default() -> Self {
|
||||
Calculator::with_settings(CalculatorSettings::default())
|
||||
.expect("Default calculator config is corrupt! Cannot start.")
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
stack: vec![1000.0, 1200.32].into_iter().collect(),
|
||||
state: CalculatorState::Normal,
|
||||
undo_buf: vec![],
|
||||
redo_buf: vec![],
|
||||
active_macros: HashSet::new(),
|
||||
registers: CalculatorRegisters::new(),
|
||||
macros: [
|
||||
(
|
||||
'e',
|
||||
CalculatorMacro {
|
||||
help: String::from("Empty"),
|
||||
value: String::from(""),
|
||||
},
|
||||
),
|
||||
(
|
||||
'1',
|
||||
CalculatorMacro {
|
||||
help: String::from("Push 1"),
|
||||
value: String::from("1 "),
|
||||
},
|
||||
),
|
||||
(
|
||||
'0',
|
||||
CalculatorMacro {
|
||||
help: String::from("Push 0"),
|
||||
value: String::from("0 "),
|
||||
},
|
||||
),
|
||||
(
|
||||
't',
|
||||
CalculatorMacro {
|
||||
help: String::from("Test"),
|
||||
value: String::from("m1m0\\m1m1\\\\"),
|
||||
},
|
||||
),
|
||||
(
|
||||
'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*/",
|
||||
),
|
||||
},
|
||||
),
|
||||
(
|
||||
's',
|
||||
CalculatorMacro {
|
||||
help: String::from("Sample data"),
|
||||
value: String::from("\\\\2 5 3n"),
|
||||
},
|
||||
),
|
||||
]
|
||||
.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::Degrees,
|
||||
display_mode: CalculatorDisplayMode::Default(None),
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Calculator {
|
||||
pub fn take_input(&mut self, c: char) -> CalculatorResult<()> {
|
||||
//for c in input.chars() {
|
||||
match &self.state {
|
||||
CalculatorState::Normal => match c {
|
||||
c @ '0'..='9' | c @ '.' | c @ 'e' => self.entry(c),
|
||||
|
@ -1,14 +1,15 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::hash_map::Iter;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum RegisterState {
|
||||
Save,
|
||||
Load,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum CalculatorState {
|
||||
Normal,
|
||||
WaitingForConstant,
|
||||
@ -17,13 +18,21 @@ pub enum CalculatorState {
|
||||
WaitingForSetting,
|
||||
}
|
||||
|
||||
impl Default for CalculatorState {
|
||||
fn default() -> Self {
|
||||
CalculatorState::Normal
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "constant")]
|
||||
pub struct CalculatorConstant {
|
||||
pub help: String,
|
||||
pub value: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "macro")]
|
||||
pub struct CalculatorMacro {
|
||||
pub help: String,
|
||||
pub value: String,
|
||||
@ -38,16 +47,154 @@ pub type CalculatorMacrosIter<'a> = Iter<'a, char, CalculatorMacro>;
|
||||
pub type CalculatorRegisters = HashMap<char, f64>;
|
||||
pub type CalculatorRegistersIter<'a> = Iter<'a, char, f64>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "angle_mode")]
|
||||
pub enum CalculatorAngleMode {
|
||||
Degrees,
|
||||
Radians,
|
||||
Grads,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CalculatorAngleMode {
|
||||
fn default() -> Self {
|
||||
CalculatorAngleMode::Degrees
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "display_mode")]
|
||||
pub enum CalculatorDisplayMode {
|
||||
Default(Option<char>),
|
||||
Scientific(usize),
|
||||
Engineering(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::Scientific(p) => write!(f, "SCI({})", p),
|
||||
CalculatorDisplayMode::Engineering(p) => write!(f, "ENG({})", p),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CalculatorDisplayMode {
|
||||
fn default() -> Self {
|
||||
CalculatorDisplayMode::Default(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
use confy::ConfyError;
|
||||
use std::fmt;
|
||||
|
||||
pub type CalculatorResult<T> = Result<T, CalculatorError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CalculatorError {
|
||||
ArithmeticError,
|
||||
NotEnoughStackEntries,
|
||||
@ -15,6 +17,8 @@ pub enum CalculatorError {
|
||||
RecursiveMacro(char),
|
||||
ParseError,
|
||||
PrecisionTooHigh,
|
||||
SaveError(Option<ConfyError>),
|
||||
LoadError(Option<ConfyError>),
|
||||
}
|
||||
|
||||
impl fmt::Display for CalculatorError {
|
||||
@ -34,6 +38,10 @@ impl fmt::Display for CalculatorError {
|
||||
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(e)) => write!(f, "Could not save: {}", e),
|
||||
CalculatorError::LoadError(None) => write!(f, "Could not load"),
|
||||
CalculatorError::LoadError(Some(e)) => write!(f, "Could not load: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
33
src/main.rs
33
src/main.rs
@ -4,7 +4,7 @@
|
||||
mod calc;
|
||||
mod util;
|
||||
|
||||
use calc::constants::{CalculatorAngleMode, CalculatorDisplayMode, CalculatorState, RegisterState};
|
||||
use calc::constants::{CalculatorDisplayMode, CalculatorState, RegisterState};
|
||||
use calc::errors::CalculatorResult;
|
||||
use calc::Calculator;
|
||||
use std::cmp;
|
||||
@ -41,9 +41,13 @@ struct App {
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> App {
|
||||
let (calculator, error_msg) = match Calculator::load_config() {
|
||||
Ok(c) => (c, None),
|
||||
Err(e) => (Calculator::default(), Some(format!("{}", e))),
|
||||
};
|
||||
App {
|
||||
calculator: Calculator::default(),
|
||||
error_msg: None,
|
||||
calculator,
|
||||
error_msg,
|
||||
state: AppState::Calculator,
|
||||
current_macro: None,
|
||||
}
|
||||
@ -81,20 +85,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
Span::styled(e, Style::default().add_modifier(Modifier::RAPID_BLINK)),
|
||||
],
|
||||
(None, AppState::Calculator) => {
|
||||
// TODO: There has to be a better way than making strings each time
|
||||
let display_mode_str = match app.calculator.get_display_mode() {
|
||||
CalculatorDisplayMode::Default(None) => String::from("DEF"),
|
||||
CalculatorDisplayMode::Default(Some(c)) => format!("DEF({})", c),
|
||||
CalculatorDisplayMode::Scientific(p) => format!("SCI({})", p),
|
||||
CalculatorDisplayMode::Engineering(p) => format!("ENG({})", p),
|
||||
};
|
||||
|
||||
let angle_mode_str = match app.calculator.get_angle_mode() {
|
||||
CalculatorAngleMode::Degrees => String::from("DEG"),
|
||||
CalculatorAngleMode::Radians => String::from("RAD"),
|
||||
CalculatorAngleMode::Grads => String::from("GRD"),
|
||||
};
|
||||
|
||||
vec![
|
||||
Span::raw("Press "),
|
||||
Span::styled("q", Style::default().add_modifier(Modifier::BOLD)),
|
||||
@ -102,7 +92,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
Span::styled("h", Style::default().add_modifier(Modifier::BOLD)),
|
||||
Span::raw(format!(
|
||||
" for help - [{}] [{}]",
|
||||
display_mode_str, angle_mode_str
|
||||
app.calculator.get_display_mode(),
|
||||
app.calculator.get_angle_mode()
|
||||
)),
|
||||
]
|
||||
}
|
||||
@ -293,6 +284,12 @@ fn handle_key(app: &mut App, events: &Events, key: Key) -> CalculatorResult<bool
|
||||
Key::Char('q') => {
|
||||
return Ok(true);
|
||||
}
|
||||
Key::Ctrl('s') => {
|
||||
app.calculator.save_config()?;
|
||||
}
|
||||
Key::Ctrl('l') => {
|
||||
app.calculator = Calculator::load_config()?;
|
||||
}
|
||||
Key::Char('h') => {
|
||||
app.state = AppState::Help;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user