ic/
app.rs

1use num_rational::Ratio;
2
3use crate::{ClassifiedIris, UnclassifiedIris};
4
5pub mod args {
6    //! Facilitates usage of this app's arguments.
7
8    use crate::util;
9
10    #[derive(clap::Parser, Debug)]
11    #[command(version, about, long_about = crate::executable_desc!())]
12    pub struct AppArgs {
13        #[cfg(feature = "knn")]
14        /// `k` that is used in KNN algorithm used by this iris classifier.
15        #[arg(short, long, default_value_t = 3)]
16        pub k_for_knn: usize,
17
18        /// Delimiter used for provided floating point values.
19        ///
20        /// Iris data should be provided in CSV format with separator being optionally overwriten by this option.
21        #[arg(short, long, default_value_t = util::AsciiChar7Bit::COMMA)]
22        pub separator: util::AsciiChar7Bit,
23
24        /// Measures this classifier's accuracy using testing irises data.
25        #[arg(short = 'a', long, default_value_t = true)]
26        pub run_accuracy_measure: bool,
27    }
28}
29pub mod cfg {
30    //! Defines app's configuration.
31
32    use core::panic;
33    use std::sync::OnceLock;
34
35    use derive_more::{Constructor, Deref, DerefMut};
36
37    /// The only app configuration object.
38    pub static APP_CFG: OnceLock<AppCfg> = OnceLock::new();
39
40    /// Returns the global app configuration.
41    ///
42    /// # Panics
43    /// * If it hasn't been initialized.
44    pub fn app_cfg() -> &'static AppCfg {
45        if let Some(app_cfg) = APP_CFG.get() {
46            return app_cfg;
47        } else {
48            panic!("Logical error: app config used before being initialized.")
49        }
50    }
51
52    /// App configuration.
53    #[derive(Constructor, Debug, Deref, DerefMut)]
54    pub struct AppCfg {
55        app_args: crate::AppArgs,
56    }
57}
58/// Measures accuracy of provided iris classifier.
59pub fn run_accuracy_measure<F>(iris_classifier: F) -> Result<(), anyhow::Error>
60where
61    F: (Fn(UnclassifiedIris) -> ClassifiedIris) + Send + Sync,
62{
63    use crate::PATH_TO_TESTING_IRISES;
64
65    let testing_irises = crate::read::testing_irises()?;
66
67    let Some(all_irises_count) = std::num::NonZeroUsize::new(testing_irises.len()) else {
68        eprintln!("Classification accuracy for \"{PATH_TO_TESTING_IRISES}\" couldn't be measured, due to file not containing any iris case.");
69        // It is not considered error, just a lack of measurement.
70        return Ok(());
71    };
72    let unclassified_t_irises: Vec<UnclassifiedIris> = testing_irises
73        .iter()
74        .map(|classified| classified.parameters)
75        .collect();
76    let reclassified_t_irises = crate::classify_irises(iris_classifier, unclassified_t_irises);
77    let good_classifications_count = reclassified_t_irises
78        .iter()
79        .zip(testing_irises.iter())
80        .filter(|&(ri, ti)| ri.classification == ti.classification)
81        .count();
82    eprintln!(
83        "Classification accuracy for \"{PATH_TO_TESTING_IRISES}\" is {} .",
84        Ratio::new(good_classifications_count, all_irises_count.get())
85    );
86    Ok(())
87}