exercises

Log | Files | Refs | README

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 }