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

View File

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