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