From 79ed8a52671077ef72da7db3b57e78132213c8c6 Mon Sep 17 00:00:00 2001
From: "hinto.janai" <hinto.janai@protonmail.com>
Date: Fri, 29 Mar 2024 20:21:17 -0400
Subject: [PATCH] add `{Benchmarker, Config, Stats}` and main()

---
 database/benchmark/src/benchmarks.rs | 99 ++++++++++++++++++++++++++++
 database/benchmark/src/cli.rs        | 74 ++++++++++++---------
 database/benchmark/src/config.rs     | 32 ++++++---
 database/benchmark/src/main.rs       | 43 ++++++++++--
 database/benchmark/src/state.rs      | 13 ++++
 5 files changed, 214 insertions(+), 47 deletions(-)
 create mode 100644 database/benchmark/src/benchmarks.rs
 create mode 100644 database/benchmark/src/state.rs

diff --git a/database/benchmark/src/benchmarks.rs b/database/benchmark/src/benchmarks.rs
new file mode 100644
index 00000000..6abead93
--- /dev/null
+++ b/database/benchmark/src/benchmarks.rs
@@ -0,0 +1,99 @@
+//! TODO
+
+//---------------------------------------------------------------------------------------------------- Import
+use std::{
+    borrow::Cow,
+    collections::BTreeSet,
+    path::{Path, PathBuf},
+};
+
+use serde::{Deserialize, Serialize};
+use strum::{
+    Display, EnumCount, EnumIs, EnumIter, EnumString, IntoStaticStr, VariantArray, VariantIterator,
+};
+
+use cuprate_database::ConcreteEnv;
+
+use crate::config::Config;
+
+//---------------------------------------------------------------------------------------------------- TODO
+/// TODO
+#[derive(
+    Clone,
+    Copy,
+    PartialEq,
+    PartialOrd,
+    Ord,
+    Eq,
+    Serialize,
+    Deserialize,
+    Debug,
+    Hash,
+    Display,
+    EnumCount,
+    EnumIs,
+    EnumIter,
+    EnumString,
+    IntoStaticStr,
+    VariantArray,
+)]
+pub(crate) enum Benchmarks {
+    /// TODO
+    EnvOpen,
+}
+
+//---------------------------------------------------------------------------------------------------- Stats
+/// TODO
+#[derive(Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize, Debug)]
+struct Stats {
+    /// TODO
+    env_open: Option<f32>,
+}
+
+impl Stats {
+    /// TODO
+    const fn new() -> Self {
+        Self { env_open: None }
+    }
+}
+
+//---------------------------------------------------------------------------------------------------- TODO
+/// TODO
+pub(crate) struct Benchmarker {
+    /// TODO
+    env: ConcreteEnv,
+    /// TODO
+    config: Config,
+    /// TODO
+    stats: Stats,
+}
+
+//---------------------------------------------------------------------------------------------------- Drop
+impl Drop for Benchmarker {
+    fn drop(&mut self) {
+        let stats_json = serde_json::to_string_pretty(&self.stats).unwrap();
+        println!("{stats_json}");
+    }
+}
+
+//---------------------------------------------------------------------------------------------------- Impl
+impl Benchmarker {
+    /// TODO
+    pub(crate) const fn new(env: ConcreteEnv, config: Config) -> Self {
+        Self {
+            env,
+            config,
+            stats: Stats::new(),
+        }
+    }
+
+    /// TODO
+    pub(crate) fn bench(self) {
+        todo!()
+    }
+
+    /// TODO
+    pub(crate) fn todo(&mut self) {
+        println!("TODO");
+    }
+}
diff --git a/database/benchmark/src/cli.rs b/database/benchmark/src/cli.rs
index 36420b14..43a2b6a2 100644
--- a/database/benchmark/src/cli.rs
+++ b/database/benchmark/src/cli.rs
@@ -5,6 +5,8 @@ use std::path::PathBuf;
 
 use clap::{crate_name, Args, Parser, Subcommand};
 
+use cuprate_helper::fs::cuprate_database_dir;
+
 use crate::config::Config;
 
 //---------------------------------------------------------------------------------------------------- Constants
@@ -16,60 +18,70 @@ use crate::config::Config;
 /// - parsing/validating input values
 /// - routing certain `--flags` to function paths (and exiting)
 /// - possibly handing `Config` back off to `main()` for continued execution
-#[derive(Parser)]
+#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Parser)]
 #[allow(clippy::struct_excessive_bools)]
 pub struct Cli {
-    //------------------------------------------------------------------------------ TODO
+    //------------------------------------------------------------------------------ Config options
     /// Set filter level for console logs.
     #[arg(
         long,
         value_name = "OFF|ERROR|INFO|WARN|DEBUG|TRACE",
         verbatim_doc_comment
     )]
-    log_level: Option<String>, // FIXME: tracing::Level{Filter} doesn't work with clap?
+    pub(crate) log_level: Option<String>, // FIXME: tracing::Level{Filter} doesn't work with clap?
+
+    /// TODO
+    #[arg(long, value_name = "PATH", verbatim_doc_comment)]
+    pub(crate) config: Option<PathBuf>,
+
+    /// TODO
+    #[arg(long, value_name = "PATH", verbatim_doc_comment)]
+    pub(crate) db_config: Option<PathBuf>,
 
     //------------------------------------------------------------------------------ Early Return
-    // These are flags that do something
-    // then immediately return, e.g `--docs`.
+    // These are flags that do something then immediately return.
     //
     // Regardless of other flags provided, these will force a return.
     #[arg(long, verbatim_doc_comment)]
-    /// Print the configuration `cuprate` would have used, but don't actually startup
+    /// Print the configuration `cuprate-database-benchmark`
+    /// would have used, but don't actually startup.
     ///
     /// This will go through the regular process of:
-    ///   - Reading disk for config
     ///   - Reading command-line
-    ///   - Merging options together
-    ///   - Validating options
+    ///   - Reading disk for config
     ///
-    /// and then print them out as TOML, and exit.
-    dry_run: bool,
+    /// and then print the config out as TOML, and exit.
+    pub(crate) dry_run: bool,
 
     #[arg(long, verbatim_doc_comment)]
-    /// Print the PATHs used by `cuprate`
-    ///
-    /// All data saved by `cuprate` is saved in these directories.
-    /// For more information, see: <https://TODO>
-    path: bool,
+    /// Print the PATH used by `cuprate-database`.
+    pub(crate) path: bool,
 
     #[arg(long, verbatim_doc_comment)]
-    /// Delete all `cuprate` files that are on disk
+    /// Delete all `cuprated-database` files that are on disk.
     ///
-    /// This deletes all `daemon` Cuprate folders.
-    /// The PATHs deleted will be printed on success.
-    delete: bool,
+    /// This deletes the PATH returned by `--path`, aka, the
+    /// directory that stores all database related files.
+    pub(crate) delete: bool,
 
     #[arg(short, long)]
-    /// Print version and exit.
-    version: bool,
+    /// Print various stats and exit.
+    pub(crate) version: bool,
 }
 
 //---------------------------------------------------------------------------------------------------- CLI default
-impl Default for Cli {
-    fn default() -> Self {
-        todo!()
-    }
-}
+// impl Default for Cli {
+//     fn default() -> Self {
+//         Self {
+//             log_level: None,
+//             db_config: None,
+//             dry_run: false,
+//             path: false,
+//             delete: false,
+//             version: false,
+//         }
+//     }
+// }
 
 //---------------------------------------------------------------------------------------------------- CLI argument handling
 impl Cli {
@@ -93,20 +105,20 @@ impl Cli {
 
         //-------------------------------------------------- Version.
         if self.version {
-            println!("TODO");
+            todo!();
             return Err(0);
         }
 
         //-------------------------------------------------- Path.
         if self.path {
-            let p: PathBuf = todo!();
-            println!("{}", p.display());
+            let path = cuprate_database_dir();
+            println!("{}", path.display());
             return Err(0);
         }
 
         //-------------------------------------------------- Delete.
         if self.delete {
-            let path = cuprate_helper::fs::cuprate_database_dir();
+            let path = cuprate_database_dir();
 
             if path.exists() {
                 println!(
diff --git a/database/benchmark/src/config.rs b/database/benchmark/src/config.rs
index e7d93379..9828a640 100644
--- a/database/benchmark/src/config.rs
+++ b/database/benchmark/src/config.rs
@@ -3,29 +3,41 @@
 //---------------------------------------------------------------------------------------------------- Import
 use std::{
     borrow::Cow,
+    collections::BTreeSet,
     path::{Path, PathBuf},
 };
 
+use serde::{Deserialize, Serialize};
+use strum::IntoEnumIterator;
+
+use crate::{benchmarks::Benchmarks, cli::Cli};
+
 //---------------------------------------------------------------------------------------------------- Config
 /// TODO
-#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-pub struct Config {}
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Serialize, Deserialize)]
+pub struct Config {
+    /// TODO
+    iterations: usize,
+    /// TODO
+    benchmark_set: BTreeSet<Benchmarks>,
+}
 
 impl Config {
     /// Create a new [`Config`] with sane default settings.
-    pub fn new() -> Self {
+    pub(crate) fn new() -> Self {
+        Self {
+            iterations: 100_000,
+            benchmark_set: Benchmarks::iter().collect(),
+        }
+    }
+
+    /// TODO
+    pub(crate) fn merge(&mut self, cli: &Cli) {
         todo!()
     }
 }
 
 impl Default for Config {
-    /// Same as `Self::new(None)`.
-    ///
-    /// ```rust
-    /// # use cuprate_database_benchmark::config::*;
-    /// assert_eq!(Config::default(), Config::new(None));
-    /// ```
     fn default() -> Self {
         Self::new()
     }
diff --git a/database/benchmark/src/main.rs b/database/benchmark/src/main.rs
index 5ba79563..01e62766 100644
--- a/database/benchmark/src/main.rs
+++ b/database/benchmark/src/main.rs
@@ -69,14 +69,18 @@
 // Import private modules, export public types.
 //
 // Documentation for each module is located in the respective file.
+mod benchmarks;
 mod cli;
 mod config;
 mod constants;
 mod free;
+mod state;
 
 //---------------------------------------------------------------------------------------------------- Private
 
 //---------------------------------------------------------------------------------------------------- Import
+use cuprate_database::Env;
+
 use crate::{cli::Cli, config::Config};
 
 //---------------------------------------------------------------------------------------------------- Main
@@ -86,13 +90,40 @@ fn main() {
         // Some arguments were passed, run all the `clap` code.
         Cli::init()
     } else {
-        // No arguments were passed, use the default config.
+        // No arguments were passed, use the default.
         Cli::default()
     };
 
-    // // If `dry_run`, print config/stats/etc and exit cleanly.
-    // if config.dry_run {
-    //     println!("{}", serde_json::to_string_pretty(CONFIG).unwrap());
-    //     std::process::exit(0);
-    // }
+    // Load the benchmark config.
+    let config = if let Some(path) = cli.config.as_ref() {
+        let bytes = std::fs::read(path).unwrap();
+        let mut disk: Config = toml_edit::de::from_slice(&bytes).unwrap();
+        disk.merge(&cli);
+        disk
+    } else {
+        Config::new()
+    };
+
+    // Load the database config.
+    let db_config = if let Some(path) = cli.db_config {
+        let bytes = std::fs::read(path).unwrap();
+        toml_edit::de::from_slice(&bytes).unwrap()
+    } else {
+        cuprate_database::config::Config::new(None)
+    };
+
+    // Print config before starting.
+    println!("{config:#?}");
+
+    // If `dry_run`, exit cleanly.
+    if cli.dry_run {
+        std::process::exit(0);
+    }
+
+    // Create database.
+    let env = cuprate_database::ConcreteEnv::open(db_config).unwrap();
+
+    // Start benchmarking/tests.
+    let mut benchmarker = crate::benchmarks::Benchmarker::new(env, todo!());
+    benchmarker.todo();
 }
diff --git a/database/benchmark/src/state.rs b/database/benchmark/src/state.rs
new file mode 100644
index 00000000..45fa7521
--- /dev/null
+++ b/database/benchmark/src/state.rs
@@ -0,0 +1,13 @@
+//! TODO
+
+//---------------------------------------------------------------------------------------------------- Import
+use std::{
+    borrow::Cow,
+    path::{Path, PathBuf},
+};
+
+//---------------------------------------------------------------------------------------------------- Config
+/// TODO
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub struct State {}