Browse Source

Add CLI config file

Mohammed Anas 2 years ago
parent
commit
518a709799
3 changed files with 130 additions and 8 deletions
  1. 76 0
      Cargo.lock
  2. 2 0
      numbat-cli/Cargo.toml
  3. 52 8
      numbat-cli/src/main.rs

+ 76 - 0
Cargo.lock

@@ -466,6 +466,12 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
 
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
 [[package]]
 name = "errno"
 version = "0.3.5"
@@ -554,6 +560,12 @@ version = "1.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
 
+[[package]]
+name = "hashbrown"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+
 [[package]]
 name = "heck"
 version = "0.4.1"
@@ -599,6 +611,16 @@ dependencies = [
  "unicode-normalization",
 ]
 
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
 [[package]]
 name = "insta"
 version = "1.34.0"
@@ -807,6 +829,8 @@ dependencies = [
  "numbat",
  "predicates",
  "rustyline",
+ "serde",
+ "toml",
 ]
 
 [[package]]
@@ -1202,6 +1226,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_spanned"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.8"
@@ -1333,6 +1366,40 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
+[[package]]
+name = "toml"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.20.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
 [[package]]
 name = "typenum"
 version = "1.17.0"
@@ -1659,6 +1726,15 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
 
+[[package]]
+name = "winnow"
+version = "0.5.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "yaml-rust"
 version = "0.4.5"

+ 2 - 0
numbat-cli/Cargo.toml

@@ -19,6 +19,8 @@ dirs = "5"
 numbat = { version = "1.7.0", path = "../numbat" }
 colored = "2"
 itertools = "0.11"
+toml = { version = "0.8.6", features = ["parse"] }
+serde = { version = "1.0.190", features = ["derive"] }
 
 [dependencies.clap]
 version = "4"

+ 52 - 8
numbat-cli/src/main.rs

@@ -24,12 +24,36 @@ use rustyline::{
     Validator,
 };
 use rustyline::{EventHandler, Highlighter, KeyCode, KeyEvent, Modifiers};
+use serde::Deserialize;
+use toml;
 
 use std::io::IsTerminal;
 use std::path::PathBuf;
 use std::sync::{Arc, Mutex};
 use std::{fs, thread};
 
+#[derive(Deserialize, PartialEq, Eq, Default)]
+#[serde(rename_all = "kebab-case")]
+enum ExchangeRateFetchingPolicy {
+    #[default]
+    Prefetch,
+    FetchOnFirstUse,
+    DontFetch,
+}
+
+#[derive(Deserialize, Default)]
+#[serde(rename_all = "kebab-case")]
+struct Config {
+    exchange_rate_fetching_policy: ExchangeRateFetchingPolicy,
+    quiet: bool,
+    #[serde(default = "default_prompt")]
+    prompt: String,
+}
+
+fn default_prompt() -> String {
+    ">>> ".to_string()
+}
+
 #[derive(Debug, PartialEq, Eq)]
 pub enum ExitStatus {
     Success,
@@ -38,8 +62,6 @@ pub enum ExitStatus {
 
 type ControlFlow = std::ops::ControlFlow<ExitStatus>;
 
-const PROMPT: &str = ">>> ";
-
 #[derive(Debug, Clone, Copy, PartialEq, ValueEnum)]
 enum PrettyPrintMode {
     Always,
@@ -111,6 +133,7 @@ struct NumbatHelper {
 
 struct Cli {
     args: Args,
+    config: Config,
     context: Arc<Mutex<Context>>,
 }
 
@@ -134,6 +157,7 @@ impl Cli {
         Self {
             context: Arc::new(Mutex::new(context)),
             args,
+            config: Config::default(),
         }
     }
 
@@ -141,6 +165,17 @@ impl Cli {
         let load_prelude = !self.args.no_prelude;
         let load_init = !(self.args.no_prelude || self.args.no_init);
 
+        let user_config_path = Self::get_config_path().join("config.toml");
+
+        if let Ok(contents) = fs::read_to_string(&user_config_path) {
+            match toml::from_str(&contents) {
+                Ok(config) => self.config = config,
+                Err(_) => {
+                    bail!("Failed to parse config file")
+                }
+            }
+        }
+
         if load_prelude {
             let result = self.parse_and_evaluate(
                 "use prelude",
@@ -169,16 +204,25 @@ impl Cli {
             }
         }
 
-        if load_prelude {
+        if load_prelude
+            && self.config.exchange_rate_fetching_policy != ExchangeRateFetchingPolicy::DontFetch
+        {
             {
                 self.context
                     .lock()
                     .unwrap()
                     .load_currency_module_on_demand(true);
             }
-            thread::spawn(move || {
-                numbat::Context::prefetch_exchange_rates();
-            });
+
+            match self.config.exchange_rate_fetching_policy {
+                ExchangeRateFetchingPolicy::Prefetch => {
+                    thread::spawn(move || {
+                        numbat::Context::prefetch_exchange_rates();
+                    });
+                }
+                ExchangeRateFetchingPolicy::FetchOnFirstUse
+                | ExchangeRateFetchingPolicy::DontFetch => {}
+            }
         }
 
         let pretty_print_mode =
@@ -226,7 +270,7 @@ impl Cli {
     fn repl(&mut self) -> Result<()> {
         let interactive = std::io::stdin().is_terminal();
         let history_path = self.get_history_path()?;
-        let quiet_startup = self.args.quiet;
+        let quiet_startup = self.args.quiet || self.config.quiet;
 
         let mut rl = Editor::<NumbatHelper, DefaultHistory>::new()?;
         rl.set_max_history_size(1000)
@@ -273,7 +317,7 @@ impl Cli {
 
     fn repl_loop(&mut self, rl: &mut Editor<NumbatHelper, DefaultHistory>) -> Result<()> {
         loop {
-            let readline = rl.readline(PROMPT);
+            let readline = rl.readline(&self.config.prompt);
             match readline {
                 Ok(line) => {
                     if !line.trim().is_empty() {