exercises

Log | Files | Refs | README

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 }