Properly bubble errors

This commit is contained in:
Austen Adler 2021-04-26 23:56:51 -04:00
parent 5762fd868a
commit 21492876c5
2 changed files with 115 additions and 132 deletions

View File

@ -6,6 +6,7 @@ pub enum CalculatorError {
NoSuchConstant, NoSuchConstant,
NoSuchRegister, NoSuchRegister,
NoSuchMacro, NoSuchMacro,
ParseError,
} }
impl CalculatorError { impl CalculatorError {
@ -21,6 +22,7 @@ impl CalculatorError {
CalculatorError::NoSuchConstant => String::from("No such constant"), CalculatorError::NoSuchConstant => String::from("No such constant"),
CalculatorError::NoSuchRegister => String::from("No such register"), CalculatorError::NoSuchRegister => String::from("No such register"),
CalculatorError::NoSuchMacro => String::from("No such macro"), CalculatorError::NoSuchMacro => String::from("No such macro"),
CalculatorError::ParseError => String::from("Parse error"),
} }
} }
} }

View File

@ -4,7 +4,7 @@
mod calc; mod calc;
mod util; mod util;
use calc::constants::CalculatorMacro; use calc::errors::CalculatorError;
use calc::operations::CalculatorOperation; use calc::operations::CalculatorOperation;
use calc::Calculator; use calc::Calculator;
use std::cmp; use std::cmp;
@ -86,18 +86,23 @@ fn main() -> Result<(), Box<dyn Error>> {
) )
.split(f.size()); .split(f.size());
let msg = match &app.error_msg { let msg = match (&app.error_msg, &app.state) {
Some(e) => vec![ (Some(e), _) => vec![
Span::raw("Error: "), Span::raw("Error: "),
Span::styled(e, Style::default().add_modifier(Modifier::RAPID_BLINK)), Span::styled(e, Style::default().add_modifier(Modifier::RAPID_BLINK)),
], ],
None => vec![ (None, AppState::Calculator) => vec![
Span::raw("Press "), Span::raw("Press "),
Span::styled("q", Style::default().add_modifier(Modifier::BOLD)), Span::styled("q", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to exit, "), Span::raw(" to exit, "),
Span::styled("h", Style::default().add_modifier(Modifier::BOLD)), Span::styled("h", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" for help"), Span::raw(" for help"),
], ],
(None, _) => vec![
Span::raw("Press "),
Span::styled("<esc>", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to exit"),
],
}; };
let text = Text::from(Spans::from(msg)); let text = Text::from(Spans::from(msg));
@ -159,7 +164,7 @@ fn main() -> Result<(), Box<dyn Error>> {
<ret> => Dup l => Log\n\ <ret> => Dup l => Log\n\
> => Swap L => Ln\n\ > => Swap L => Ln\n\
e => E ^c => Constants\n\ e => E ^c => Constants\n\
^m => Macros rR => Registers", m => Macros rR => Registers",
}, },
f, f,
); );
@ -217,18 +222,23 @@ fn main() -> Result<(), Box<dyn Error>> {
})?; })?;
if let Event::Input(key) = events.next()? { if let Event::Input(key) = events.next()? {
if handle_key(&mut app, &events, key) { app.error_msg = match handle_key(&mut app, &events, key) {
break 'outer; // Exit the program
Ok(true) => break 'outer Ok(()),
Ok(false) => None,
Err(e) => Some(e.message()),
}; };
} }
for e in events.try_iter() { for e in events.try_iter() {
match e { match e {
Event::Input(key) => { Event::Input(key) => {
if handle_key(&mut app, &events, key) { app.error_msg = match handle_key(&mut app, &events, key) {
//return Ok(()); // Exit the program
break 'outer; Ok(true) => break 'outer Ok(()),
} Ok(false) => None,
Err(e) => Some(e.message()),
};
} }
Event::MacroEnd => app.current_macro = None, Event::MacroEnd => app.current_macro = None,
_ => continue, _ => continue,
@ -237,170 +247,141 @@ fn main() -> Result<(), Box<dyn Error>> {
app.current_macro = None; app.current_macro = None;
} }
// TODO: Bubble up return Ok so we can handle saving
Ok(())
} }
fn handle_key(app: &mut App, events: &Events, key: Key) -> bool { fn handle_key(app: &mut App, events: &Events, key: Key) -> Result<bool, CalculatorError> {
match &app.state { match &app.state {
AppState::Calculator => { AppState::Calculator => match key {
app.error_msg = None; Key::Char('q') => {
match key { return Ok(true);
Key::Char('q') => { }
//process::exit(0); Key::Ctrl('c') => {
print!("XX"); app.state = AppState::Constants;
return true; }
Key::Char('r') => {
app.state = AppState::Registers(RegisterState::Load);
}
Key::Char('R') => {
app.state = AppState::Registers(RegisterState::Save);
}
Key::Char('m') => {
app.state = AppState::Macros;
}
Key::Char('h') => {
app.state = AppState::Help;
}
Key::Char(c @ '0'..='9') => {
app.input.push(c);
}
Key::Char(c @ 'e') | Key::Char(c @ '.') => {
if app.input.is_empty() {
if let Ok(f) = app.calculator.pop() {
app.input = f.to_string();
} else {
return Err(CalculatorError::NotEnoughStackEntries);
}
} }
Key::Ctrl('c') => {
app.state = AppState::Constants; if !app.input.contains(c) {
}
Key::Char('r') => {
app.state = AppState::Registers(RegisterState::Load);
}
Key::Char('R') => {
app.state = AppState::Registers(RegisterState::Save);
}
Key::Char('m') => {
app.state = AppState::Macros;
}
Key::Char('h') => {
app.state = AppState::Help;
}
Key::Char(c @ '0'..='9') => {
app.input.push(c); app.input.push(c);
} }
Key::Char(c @ 'e') | Key::Char(c @ '.') => {
if app.input.is_empty() {
if let Ok(x) = app.calculator.pop() {
app.input = x.to_string();
} else {
return false;
}
}
if !app.input.contains(c) {
app.input.push(c);
}
}
Key::Char('\n') | Key::Char(' ') => {
if app.input.is_empty() {
calc_operation(app, '\n');
} else {
let mut tmp_input = app.input.clone();
if tmp_input.ends_with('e') {
let tmp_input = tmp_input.push('0');
}
if let Ok(f) = tmp_input.parse::<f64>() {
if app.calculator.push(f).is_ok() {
app.input.clear();
}
}
}
}
Key::Right => {
calc_operation(app, '>');
}
Key::Down => {
if let Ok(x) = app.calculator.pop() {
app.input = x.to_string();
}
}
Key::Backspace => {
app.input.pop();
}
Key::Delete => {
app.input.clear();
}
Key::Char(c) => {
calc_operation(app, c);
}
_ => {}
} }
} Key::Char('\n') | Key::Char(' ') => {
if app.input.is_empty() {
calc_operation(app, '\n');
} else {
let mut tmp_input = app.input.clone();
if tmp_input.ends_with('e') {
let tmp_input = tmp_input.push('0');
}
if let Ok(f) = tmp_input.parse::<f64>() {
if app.calculator.push(f).is_ok() {
app.input.clear();
}
}
}
}
Key::Right => {
calc_operation(app, '>');
}
Key::Down => {
if let Ok(x) = app.calculator.pop() {
app.input = x.to_string();
}
}
Key::Backspace => {
app.input.pop();
}
Key::Delete => {
app.input.clear();
}
Key::Char(c) => {
calc_operation(app, c);
}
_ => {}
},
AppState::Help => match key { AppState::Help => match key {
Key::Esc | Key::Char('q') => { Key::Esc => {
app.state = AppState::Calculator; app.state = AppState::Calculator;
} }
_ => {} _ => {}
}, },
AppState::Constants => match key { AppState::Constants => match key {
Key::Esc | Key::Char('q') => { Key::Esc => {
app.state = AppState::Calculator; app.state = AppState::Calculator;
} }
Key::Char(c) => { Key::Char(c) => {
app.error_msg = match app.calculator.push_constant(c) { app.calculator.push_constant(c)?;
Err(e) => Some(e.message()), app.input.clear();
Ok(()) => { app.state = AppState::Calculator;
app.input.clear();
app.state = AppState::Calculator;
None
}
}
} }
_ => {} _ => {}
}, },
AppState::Registers(task) => match key { AppState::Registers(task) => match key {
Key::Esc | Key::Char('q') => { Key::Esc => {
app.state = AppState::Calculator; app.state = AppState::Calculator;
} }
Key::Char(c) => match task { Key::Char(c) => {
RegisterState::Save => { match task {
app.error_msg = match app.calculator.save_register(c) { RegisterState::Save => {
Err(e) => Some(e.message()), app.calculator.save_register(c)?;
Ok(()) => { }
app.input.clear(); RegisterState::Load => {
app.state = AppState::Calculator; app.calculator.push_register(c)?;
None
}
} }
} }
RegisterState::Load => { app.input.clear();
app.error_msg = match app.calculator.push_register(c) { app.state = AppState::Calculator;
Err(e) => Some(e.message()), }
Ok(()) => {
app.input.clear();
app.state = AppState::Calculator;
None
}
}
}
},
_ => {} _ => {}
}, },
AppState::Macros => match key { AppState::Macros => match key {
Key::Esc | Key::Char('q') => { Key::Esc => {
app.state = AppState::Calculator; app.state = AppState::Calculator;
} }
Key::Char(c) => { Key::Char(c) => {
if !app.input.is_empty() { if !app.input.is_empty() {
//TODO: A better way to do this //TODO: A better way to do this
if let Ok(f) = app.input.parse::<f64>() { if let Ok(f) = app.input.parse::<f64>() {
if app.calculator.push(f).is_ok() { match app.calculator.push(f) {
app.input.clear(); Ok(()) => app.input.clear(),
} else { Err(e) => return Err(e),
return false;
} }
} else { } else {
return false; return Err(CalculatorError::ParseError);
} }
} }
// TODO: Handle macros internally to the calculator // TODO: Handle macros internally to the calculator
app.error_msg = match app.calculator.get_macro(c) { let mac = app.calculator.get_macro(c)?;
Ok(CalculatorMacro { value, .. }) => { events.fill_event_buf(mac.value);
// let value = *value; app.state = AppState::Calculator;
events.fill_event_buf(value);
app.state = AppState::Calculator;
None
}
Err(e) => Some(e.message()),
}
} }
_ => {} _ => {}
}, },
} }
return false; return Ok(false);
} }
fn calc_operation(app: &mut App, c: char) { fn calc_operation(app: &mut App, c: char) {