exercises

Log | Files | Refs | README

adapters.rs (3117B)


      1 //! Adapter Pattern
      2 
      3 use std::{collections::HashMap, f64::consts::PI};
      4 
      5 use crate::{number::AngleMode, token::VariableToken, utilities::Expression};
      6 
      7 pub trait ScientificOperations {
      8     fn sin(&self, angle: f64) -> f64;
      9     fn cos(&self, angle: f64) -> f64;
     10     fn tan(&self, angle: f64) -> f64;
     11     fn log(&self, value: f64, base: f64) -> Result<f64, String>;
     12 }
     13 
     14 pub struct StandardScientificOperations {
     15     pub angle_mode: AngleMode,
     16 }
     17 
     18 impl ScientificOperations for StandardScientificOperations {
     19     fn sin(&self, angle: f64) -> f64 {
     20         match self.angle_mode {
     21             AngleMode::Radians => angle.sin(),
     22             AngleMode::Degrees => (angle * PI / 180.0).sin(),
     23         }
     24     }
     25 
     26     fn cos(&self, angle: f64) -> f64 {
     27         match self.angle_mode {
     28             AngleMode::Radians => angle.cos(),
     29             AngleMode::Degrees => (angle * PI / 180.0).cos(),
     30         }
     31     }
     32 
     33     fn tan(&self, angle: f64) -> f64 {
     34         todo!()
     35     }
     36 
     37     fn log(&self, value: f64, base: f64) -> Result<f64, String> {
     38         if value <= 0.0 {
     39             return Err("Cannot take logarithm of non-positive number".to_string());
     40         }
     41         if base <= 0.0 || base == 1.0 {
     42             return Err("Invalid logarithm base".to_string());
     43         }
     44         Ok(value.ln() / base.ln())
     45     }
     46 }
     47 
     48 pub struct ExternalLibraryAdapter {
     49     angle_mode: AngleMode,
     50 }
     51 
     52 impl ExternalLibraryAdapter {
     53     pub fn new(angle_mode: AngleMode) -> Self {
     54         Self { angle_mode }
     55     }
     56 
     57     fn convert_angle(&self, angle: f64) -> f64 {
     58         match self.angle_mode {
     59             AngleMode::Radians => angle,
     60             AngleMode::Degrees => angle * PI / 180.0,
     61         }
     62     }
     63 }
     64 
     65 impl ScientificOperations for ExternalLibraryAdapter {
     66     fn sin(&self, angle: f64) -> f64 {
     67         let converted = self.convert_angle(angle);
     68         converted.sin()
     69     }
     70 
     71     fn cos(&self, angle: f64) -> f64 {
     72         todo!()
     73     }
     74 
     75     fn tan(&self, angle: f64) -> f64 {
     76         todo!()
     77     }
     78 
     79     fn log(&self, value: f64, base: f64) -> Result<f64, String> {
     80         todo!()
     81     }
     82 }
     83 
     84 /// This struct uses a closure (Box<dyn Fn(f64) -> f64) to capture a specific
     85 /// scientific operation.
     86 pub struct ScientificFunctionExpression {
     87     operation: Box<dyn Fn(f64) -> f64>,
     88     arg_expression: Box<dyn Expression>,
     89     description: String,
     90 }
     91 
     92 impl Expression for ScientificFunctionExpression {
     93     fn evaluate(&self, variables: &HashMap<String, f64>) -> Result<f64, String> {
     94         let arg_value = self.arg_expression.evaluate(variables)?;
     95         Ok((self.operation)(arg_value))
     96     }
     97 
     98     fn to_string(&self) -> String {
     99         format!("{}({})", self.description, self.arg_expression.to_string())
    100     }
    101 }
    102 
    103 /// The move keyword transfers ownership of the `sci_ops` trait object into
    104 /// the closure.
    105 impl ScientificFunctionExpression {
    106     pub fn new_sin(sci_ops: Box<dyn ScientificOperations>, arg: Box<dyn Expression>) -> Self {
    107         let operation = Box::new(move |angle: f64| sci_ops.sin(angle));
    108         Self {
    109             operation,
    110             arg_expression: arg,
    111             description: "sin".to_string(),
    112         }
    113     }
    114 }