use crate::{number::Number, operator::Operator};
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
Number(Number),
Operator(Operator),
}
pub struct Calculator {
buffer: Vec<Token>,
display_index: usize,
cleared: bool,
editing: bool,
}
impl Calculator {
pub fn new() -> Self {
Calculator {
buffer: Vec::from([Token::Number(Number::from(0.))]),
display_index: 0,
cleared: true,
editing: false,
}
}
pub fn cleared(&self) -> bool {
self.cleared
}
pub fn clear(&mut self) {
if self.cleared {
*self = Calculator::new();
} else {
self.display_index = 0;
self.buffer[0] = Token::Number(Number::from(0.));
self.cleared = true;
}
}
pub fn output(&mut self) -> &mut Number {
match self.buffer.get_mut(self.display_index).unwrap() {
Token::Number(number) => return number,
_ => {
panic!("Display index points to a non-number")
}
}
}
fn calculate(&self) -> Result<Number, &str> {
let mut values = self.buffer.clone();
let mut index = values.len() - 1;
while index >= 2 {
if let Some(Token::Number(number_b)) = values.get(index) {
if let Some(Token::Operator(operator)) = values.get(index - 1) {
if let Some(Token::Number(number_a)) = values.get(index - 2) {
values[index - 2] = Token::Number(Number::from((operator.function)(
number_a.value(),
number_b.value(),
)))
}
}
}
index -= 2;
}
if let Some(Token::Number(result)) = values.first() {
return Ok(result.to_owned());
}
Err("")
}
pub fn active_operator(&self) -> Option<&Operator> {
if let Some(Token::Operator(operator)) = self.buffer.last() {
return Some(operator);
}
return None;
}
fn last_operator(&self) -> Option<Operator> {
for index in (0..self.buffer.len()).rev() {
if let Some(Token::Operator(operator)) = self.buffer.get(index) {
return Some(operator.to_owned());
}
}
return None;
}
pub fn submit_equals(&mut self) {
self.editing = false;
if self.buffer.len() <= 2 {
return;
}
if let Ok(result) = self.calculate() {
self.buffer[0] = Token::Number(result);
self.display_index = 0;
if self.buffer.len() > 3 {
self.buffer.drain(1..3);
}
}
}
pub fn submit_operator(&mut self, operator: Operator) {
self.editing = false;
match self.buffer.last_mut() {
Some(Token::Operator(active_operator)) => {
if active_operator.id != operator.id {
*active_operator = operator;
}
}
Some(Token::Number(..)) => {
if self.display_index > 0 {
if let Some(prev_operator) = self.last_operator() {
if prev_operator.after(&operator) {
self.buffer.push(Token::Operator(operator));
return;
}
}
self.submit_equals();
}
self.buffer.drain(1..self.buffer.len());
self.buffer.push(Token::Operator(operator));
}
None => panic!("Buffer is empty."),
}
}
pub fn submit_number(&mut self, number: u8) {
match self.buffer.last() {
Some(Token::Number(..)) => {
if self.editing {
self.output().append(number);
} else {
self.buffer[0] = Token::Number(Number::from(number));
}
}
Some(Token::Operator(..)) => {
self.buffer.push(Token::Number(Number::from(number)));
self.display_index += 2;
}
None => panic!("Buffer is empty."),
};
self.editing = true;
self.cleared = false;
}
pub fn submit_decimal(&mut self) {
if !self.editing {
self.submit_number(0);
}
self.output().decimalise();
}
pub fn submit_negative(&mut self) {
let output = self.output();
output.set_value(output.value() * -1.);
self.editing = true;
}
pub fn submit_percentage(&mut self) {
if self.display_index > 0 {
let _ = self.calculate();
}
let output = self.output();
output.set_value(output.value() / 100.);
}
}
#[cfg(test)]
mod test {
use super::*;
fn test_calculator() -> Calculator {
Calculator {
display_index: 0,
cleared: true,
editing: false,
buffer: vec![],
}
}
#[test]
fn doc_example_test() {
let mut calc = Calculator::new();
calc.submit_number(1);
calc.submit_decimal();
calc.submit_number(2);
calc.submit_operator(Operator::add());
calc.submit_number(2);
calc.submit_equals();
assert_eq!(
calc.output().value(),
3.2,
);
assert_eq!(
calc.output().to_string(),
"3.2",
);
}
#[test]
fn last_operator_none() {
let number_1 = Number::from(1.);
let mut calc = test_calculator();
calc.buffer = vec![Token::Number(number_1.clone())];
assert_eq!(
calc.last_operator(),
None,
"Should return none when no operator in the buffer."
);
}
#[test]
fn last_operator_empty() {
let calc = test_calculator();
assert_eq!(
calc.last_operator(),
None,
"Should return none when buffer is empty."
);
}
#[test]
fn submit_percentage() {
let number_1 = Number::from(1.);
let mut calc = test_calculator();
calc.buffer = vec![Token::Number(number_1.clone())];
calc.submit_percentage();
assert_eq!(
calc.output(),
&Number::from(0.01),
"Should convert output to a percentage."
);
}
#[test]
fn submit_percentage_negative() {
let number_1 = Number::from(-1.);
let mut calc = test_calculator();
calc.buffer = vec![Token::Number(number_1.clone())];
calc.submit_percentage();
assert_eq!(
calc.output(),
&Number::from(-0.01),
"Should convert output to a percentage."
);
}
}