Repository: rust-cli/config-rs (language: rust)

Issue:
Remove lazy_static
`lazy_static` can be replaced with `OnceLock` with the recent MSRV.


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index a1940446..e6204ccf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -178,7 +178,6 @@ dependencies = [
  "glob",
  "indexmap 2.2.2",
  "json5",
- "lazy_static",
  "log",
  "nom",
  "notify",
diff --git a/Cargo.toml b/Cargo.toml
index 93f39c7c..bdbfbbd0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -121,7 +121,6 @@ preserve_order = ["indexmap", "toml?/preserve_order", "serde_json?/preserve_orde
 async = ["async-trait"]
 
 [dependencies]
-lazy_static = "1.4"
 serde = "1.0"
 nom = "7"
 
diff --git a/examples/global/main.rs b/examples/global/main.rs
index 160d8817..0184be45 100644
--- a/examples/global/main.rs
+++ b/examples/global/main.rs
@@ -1,19 +1,20 @@
 #![allow(deprecated)]
 use config::Config;
-use lazy_static::lazy_static;
 use std::error::Error;
+use std::sync::OnceLock;
 use std::sync::RwLock;
 
-lazy_static! {
-    static ref SETTINGS: RwLock<Config> = RwLock::new(Config::default());
+fn settings() -> &'static RwLock<Config> {
+    static CONFIG: OnceLock<RwLock<Config>> = OnceLock::new();
+    CONFIG.get_or_init(|| RwLock::new(Config::default()))
 }
 
 fn try_main() -> Result<(), Box<dyn Error>> {
     // Set property
-    SETTINGS.write()?.set("property", 42)?;
+    settings().write()?.set("property", 42)?;
 
     // Get property
-    println!("property: {}", SETTINGS.read()?.get::<i32>("property")?);
+    println!("property: {}", settings().read()?.get::<i32>("property")?);
 
     Ok(())
 }
diff --git a/examples/static_env.rs b/examples/static_env.rs
index ba3222fc..e95cd532 100644
--- a/examples/static_env.rs
+++ b/examples/static_env.rs
@@ -1,18 +1,22 @@
+use std::sync::OnceLock;
+
 use config::Config;
 
-lazy_static::lazy_static! {
-    #[derive(Debug)]
-    pub static ref CONFIG: Config = Config::builder()
-        .add_source(config::Environment::with_prefix("APP_NAME").separator("_"))
-        .build()
-        .unwrap();
+fn config() -> &'static Config {
+    static CONFIG: OnceLock<Config> = OnceLock::new();
+    CONFIG.get_or_init(|| {
+        Config::builder()
+            .add_source(config::Environment::with_prefix("APP_NAME").separator("_"))
+            .build()
+            .unwrap()
+    })
 }
 
 /// Get a configuration value from the static configuration object
 pub fn get<'a, T: serde::Deserialize<'a>>(key: &str) -> T {
     // You shouldn't probably do it like that and actually handle that error that might happen
     // here, but for the sake of simplicity, we do it like this here
-    CONFIG.get::<T>(key).unwrap()
+    config().get::<T>(key).unwrap()
 }
 
 fn main() {
diff --git a/examples/watch/main.rs b/examples/watch/main.rs
index 7f35e869..25df2e43 100644
--- a/examples/watch/main.rs
+++ b/examples/watch/main.rs
@@ -4,22 +4,26 @@ use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher};
 use std::collections::HashMap;
 use std::path::Path;
 use std::sync::mpsc::channel;
+use std::sync::OnceLock;
 use std::sync::RwLock;
 use std::time::Duration;
 
-lazy_static::lazy_static! {
-    static ref SETTINGS: RwLock<Config> = RwLock::new({
+fn settings() -> &'static RwLock<Config> {
+    static CONFIG: OnceLock<RwLock<Config>> = OnceLock::new();
+    CONFIG.get_or_init(|| {
         let mut settings = Config::default();
-        settings.merge(File::with_name("examples/watch/Settings.toml")).unwrap();
-
         settings
-    });
+            .merge(File::with_name("examples/watch/Settings.toml"))
+            .unwrap();
+
+        RwLock::new(settings)
+    })
 }
 
 fn show() {
     println!(
         " * Settings :: \n\x1b[31m{:?}\x1b[0m",
-        SETTINGS
+        settings()
             .read()
             .unwrap()
             .clone()
@@ -58,7 +62,7 @@ fn watch() -> ! {
                 ..
             })) => {
                 println!(" * Settings.toml written; refreshing configuration ...");
-                SETTINGS.write().unwrap().refresh().unwrap();
+                settings().write().unwrap().refresh().unwrap();
                 show();
             }
 
diff --git a/src/file/format/mod.rs b/src/file/format/mod.rs
index 1242d153..055a4ce5 100644
--- a/src/file/format/mod.rs
+++ b/src/file/format/mod.rs
@@ -1,10 +1,6 @@
-// If no features are used, there is an "unused mut" warning in `ALL_EXTENSIONS`
-// BUG: ? For some reason this doesn't do anything if I try and function scope this
-#![allow(unused_mut)]
-
-use lazy_static::lazy_static;
 use std::collections::HashMap;
 use std::error::Error;
+use std::sync::OnceLock;
 
 use crate::map::Map;
 use crate::{file::FileStoredFormat, value::Value, Format};
@@ -57,10 +53,11 @@ pub enum FileFormat {
     Json5,
 }
 
-lazy_static! {
-    #[doc(hidden)]
-    // #[allow(unused_mut)] ?
-    pub static ref ALL_EXTENSIONS: HashMap<FileFormat, Vec<&'static str>> = {
+pub(crate) fn all_extensions() -> &'static HashMap<FileFormat, Vec<&'static str>> {
+    #![allow(unused_mut)] // If no features are used, there is an "unused mut" warning in `all_extensions`
+
+    static ALL_EXTENSIONS: OnceLock<HashMap<FileFormat, Vec<&'static str>>> = OnceLock::new();
+    ALL_EXTENSIONS.get_or_init(|| {
         let mut formats: HashMap<FileFormat, Vec<_>> = HashMap::new();
 
         #[cfg(feature = "toml")]
@@ -82,15 +79,15 @@ lazy_static! {
         formats.insert(FileFormat::Json5, vec!["json5"]);
 
         formats
-    };
+    })
 }
 
 impl FileFormat {
     pub(crate) fn extensions(&self) -> &'static [&'static str] {
         // It should not be possible for this to fail
         // A FileFormat would need to be declared without being added to the
-        // ALL_EXTENSIONS map.
-        ALL_EXTENSIONS.get(self).unwrap()
+        // all_extensions map.
+        all_extensions().get(self).unwrap()
     }
 
     pub(crate) fn parse(
diff --git a/src/file/source/file.rs b/src/file/source/file.rs
index 8ee5d315..0e0687c2 100644
--- a/src/file/source/file.rs
+++ b/src/file/source/file.rs
@@ -5,7 +5,7 @@ use std::io;
 use std::path::PathBuf;
 
 use crate::file::{
-    format::ALL_EXTENSIONS, source::FileSourceResult, FileSource, FileStoredFormat, Format,
+    format::all_extensions, source::FileSourceResult, FileSource, FileStoredFormat, Format,
 };
 
 /// Describes a file sourced from a file
@@ -38,7 +38,7 @@ impl FileSourceFile {
             return if let Some(format) = format_hint {
                 Ok((filename, Box::new(format)))
             } else {
-                for (format, extensions) in ALL_EXTENSIONS.iter() {
+                for (format, extensions) in all_extensions().iter() {
                     if extensions.contains(
                         &filename
                             .extension()
@@ -75,7 +75,7 @@ impl FileSourceFile {
             }
 
             None => {
-                for format in ALL_EXTENSIONS.keys() {
+                for format in all_extensions().keys() {
                     for ext in format.extensions() {
                         filename.set_extension(ext);
 


Test patch: