diff --git a/src/calc/errors.rs b/src/calc/errors.rs index 6ffa49f..64aefe1 100644 --- a/src/calc/errors.rs +++ b/src/calc/errors.rs @@ -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"), } } } diff --git a/src/main.rs b/src/main.rs index 6d5db6c..eefa1a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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> { ) .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("", 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> { => 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> { })?; 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> { 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 { 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::() { - 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::() { + 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::() { - 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) {