calculator.rs (3881B)
1 use std::sync::Arc; 2 3 use crate::{ 4 TokenFactory, 5 factory::StandardFactory, 6 number::{AngleMode, NumberFormat}, 7 token::Token, 8 }; 9 10 // /// Singleton Pattern? 11 // /// This is rarely the best solution in Rust 12 // pub struct CalculatorSettings { 13 // precision: u32, 14 // angle_mode: AngleMode, 15 // notation: NumberFormat, 16 // } 17 // impl CalculatorSettings { 18 // pub fn instance() -> &'static mut Self { 19 // static mut INSTANCE: Option<CalculatorSettings> = None; 20 21 // // The traditional singleton pattern requires unsafe, because we're 22 // // creating mutable static variable; it's also not thread-safe 23 // unsafe { 24 // INSTANCE.get_or_insert_with(|| Self { 25 // precision: 10, 26 // angle_mode: AngleMode::Radians, 27 // notation: NumberFormat::Decimal, 28 // }) 29 // } 30 // } 31 // } 32 33 /// Instead of Singleton Pattern, we will build something more robust that 34 /// actually does what we need it to do: 35 /// - this struct replaces the mutable global state 36 /// - the Default implementation provide sensible defaults 37 /// - because this struct is Clone, each calculator can have its own copy, 38 /// eliminating shared mutable state 39 #[derive(Clone, Debug, Default)] 40 pub struct CalculatorConfig { 41 precision: u32, 42 angle_mode: AngleMode, 43 notation: NumberFormat, 44 } 45 46 // Use Default trait instead 47 // impl Default for CalculatorConfig { 48 // fn default() -> Self { 49 // Self { 50 // precision: 10, 51 // angle_mode: AngleMode::Radians, 52 // notation: NumberFormat::Decimal, 53 // } 54 // } 55 // } 56 57 impl CalculatorConfig { 58 pub fn scientific() -> Self { 59 Self { 60 precision: 15, 61 angle_mode: AngleMode::Radians, 62 notation: NumberFormat::Scientific, 63 ..Default::default() 64 } 65 } 66 67 pub fn engineering() -> Self { 68 Self { 69 notation: NumberFormat::Engineering, 70 ..Default::default() 71 } 72 } 73 } 74 75 pub struct Calculator<F: TokenFactory> { 76 config: CalculatorConfig, 77 factory: F, 78 expression: Vec<Token<F::Number, F::Operator>>, 79 } 80 81 impl<F: TokenFactory> Calculator<F> { 82 pub fn new(factory: F) -> Self { 83 Self { 84 config: CalculatorConfig::default(), 85 factory, 86 expression: Vec::new(), 87 } 88 } 89 90 /// It takes in a configuration explicitly through its constructor 91 /// This dependency injection approach makes the calculator's requirement 92 /// visible in its API. It has no hidden global state that might change 93 /// unexpectedly 94 pub fn with_config(factory: F, config: CalculatorConfig) -> Self { 95 Self { 96 config, 97 factory, 98 expression: Vec::new(), 99 } 100 } 101 102 pub fn parse(&mut self, input: &str) -> Result<(), String> { 103 for token in input.split_whitespace() { 104 // Try operator first 105 if let Ok(op) = self.factory.create_operator(token) { 106 self.expression.push(Token::Operator(op)); 107 continue; 108 } 109 110 // Must be a number then 111 let num = self.factory.create_number(token)?; 112 self.expression.push(Token::Number(num)); 113 } 114 115 Ok(()) 116 } 117 } 118 119 pub struct CalculatorPool { 120 shared_config: Arc<CalculatorConfig>, 121 calculators: Vec<Calculator<StandardFactory>>, 122 } 123 124 impl CalculatorPool { 125 pub fn new(config: CalculatorConfig) -> Self { 126 Self { 127 shared_config: Arc::new(config), 128 calculators: Vec::new(), 129 } 130 } 131 132 pub fn new_calculator(&mut self) -> Calculator<StandardFactory> { 133 // let calc = Calculator::with_config(StandardFactory, (*self.shared_config).clone()); 134 // self.calculators.push(calc.clone()); 135 // calc 136 todo!() 137 } 138 }