mirror of
https://github.com/Steffo99/micronfig.git
synced 2024-11-25 09:34:19 +00:00
Complete the new crate, but...
This commit is contained in:
parent
c6df6cb3af
commit
ef0094cd2c
7 changed files with 259 additions and 9 deletions
16
Cargo.toml
16
Cargo.toml
|
@ -17,15 +17,13 @@ rustdoc-args = ["--document-private-items", "--cfg", "docsrs"]
|
|||
|
||||
|
||||
[features]
|
||||
default = ["single_envvars", "single_envfiles", "multi", "handle", "macros"]
|
||||
single_envvars = []
|
||||
single_envfiles = []
|
||||
multi = ["single_envvars", "single_envfiles"]
|
||||
handle = ["multi"]
|
||||
macros = ["lazy_static", "handle"]
|
||||
default = ["envvars", "envfiles", "envdot"]
|
||||
envvars = []
|
||||
envfiles = []
|
||||
envdot = ["regex"]
|
||||
testing = ["tempfile"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
lazy_static = { version = "1.4.0", optional = true }
|
||||
tempfile = { version = "3.5.0", optional = true }
|
||||
tempfile = { version = "3.9.0", optional = true }
|
||||
regex = { version = "1.10.2", optional = true }
|
||||
|
||||
|
|
22
src/cache.rs
Normal file
22
src/cache.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct MicronfigCache {
|
||||
#[cfg(feature = "envdot")]
|
||||
pub dotenvs: Vec<crate::sources::envdot::DotEnv>
|
||||
}
|
||||
|
||||
impl MicronfigCache {
|
||||
#[cfg(feature = "envdot")]
|
||||
pub fn add_envdot<P>(&mut self, path: P)
|
||||
where P: AsRef<Path> + Debug
|
||||
{
|
||||
self.dotenvs.push(
|
||||
crate::sources::envdot::DotEnv::from(
|
||||
path
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
143
src/lib.rs
Normal file
143
src/lib.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
pub mod sources;
|
||||
pub mod cache;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! __micronfig_last {
|
||||
[ $head:ty, $( $tail:ty, )+ ] => {
|
||||
$crate::__micronfig_last![ $( $tail, )+ ]
|
||||
};
|
||||
[ $head:ty, ] => {
|
||||
$head
|
||||
}
|
||||
}
|
||||
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Get a string directly
|
||||
///
|
||||
/// ```
|
||||
/// micronfig::config! {
|
||||
/// MY_STRING_A: String,
|
||||
/// MY_STRING_B: String
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Parse envvar as a number
|
||||
///
|
||||
/// ```
|
||||
/// micronfig::config! {
|
||||
/// MY_UNSIGNED_NUMBER: u32,
|
||||
/// MY_SIGNED_NUMBER: i32
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Parse string as an IpAddr
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::IpAddr;
|
||||
///
|
||||
/// micronfig::config! {
|
||||
/// MY_IP_ADDR: IpAddr
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Parse string as a number, then convert it into an Ipv4Addr
|
||||
///
|
||||
/// ```
|
||||
/// use std::net::Ipv4Addr;
|
||||
///
|
||||
/// micronfig::config! {
|
||||
/// MY_NUMERIC_IP_ADDR: Ipv4Addr
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Parse string with custom logic resulting into a number
|
||||
///
|
||||
/// ```
|
||||
/// struct CustomConverter(u8);
|
||||
///
|
||||
/// impl From<String> for CustomConverter {
|
||||
/// fn from(value: String) -> Self {
|
||||
/// Self(123)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl Into<u8> for CustomConverter {
|
||||
/// fn into(self) -> u8 {
|
||||
/// self.0
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// micronfig::config! {
|
||||
/// MY_ONETWOTHREE: CustomConverter => u8,
|
||||
/// MY_ONETWOTHREE_BUT_EXPLICIT: String => CustomConverter => u8
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! config {
|
||||
{ $( $identifier:ident: $( $conversion:ty )=>* ),+ } => {
|
||||
static __micronfig_cache: std::sync::OnceLock<$crate::cache::MicronfigCache> = std::sync::OnceLock::new();
|
||||
|
||||
fn __micronfig_init_cache() -> $crate::cache::MicronfigCache {
|
||||
let mut this = $crate::cache::MicronfigCache::default();
|
||||
|
||||
if cfg!(feature = "envdot") {
|
||||
this.add_envdot("./.env");
|
||||
this.add_envdot("./.env.local");
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
fn __micronfig_get(key: &str) -> Option<String> {
|
||||
let mut value: Option<String> = None;
|
||||
|
||||
if cfg!(feature = "envfiles") && value.is_none() {
|
||||
value = $crate::sources::envfiles::get(format!("{key}_FILE"));
|
||||
}
|
||||
|
||||
if cfg!(feature = "envvars") && value.is_none() {
|
||||
value = $crate::sources::envvars::get(&key);
|
||||
}
|
||||
|
||||
if cfg!(feature = "envdot") && value.is_none() {
|
||||
let cache = __micronfig_cache.get_or_init(__micronfig_init_cache);
|
||||
|
||||
for dotenv in cache.dotenvs.iter() {
|
||||
value = $crate::sources::envdot::get(dotenv, &key);
|
||||
if value.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
$(
|
||||
pub(self) mod $identifier {
|
||||
pub static lock: std::sync::OnceLock<Option< $crate::__micronfig_last![ $( $conversion, )+ ] >> = std::sync::OnceLock::new();
|
||||
}
|
||||
|
||||
pub(crate) fn $identifier () -> &'static Option< $crate::__micronfig_last![ $( $conversion, )+ ] > {
|
||||
$identifier::lock.get_or_init(|| {
|
||||
let key = stringify!($identifier);
|
||||
let value = __micronfig_get(key);
|
||||
|
||||
$(
|
||||
let value: Option<$conversion> = value.map(Into::into);
|
||||
)+
|
||||
|
||||
value
|
||||
})
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
config! {
|
||||
SOMETHING: String
|
||||
}
|
50
src/sources/envdot.rs
Normal file
50
src/sources/envdot.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
//! Variables defined in specific dotenv files.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Clone, Default, PartialEq, Eq, Debug)]
|
||||
pub struct DotEnv(
|
||||
HashMap<String, String>
|
||||
);
|
||||
|
||||
impl DotEnv {
|
||||
pub fn var(&self, key: &str) -> Option<&String> {
|
||||
self.0.get(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> From<P> for DotEnv
|
||||
where P: AsRef<Path> + Debug
|
||||
{
|
||||
fn from(value: P) -> Self {
|
||||
let mut file = File::open(&value)
|
||||
.expect(&*format!("to be able to open {value:?}"));
|
||||
|
||||
let mut contents: String = String::new();
|
||||
file.read_to_string(&mut contents)
|
||||
.expect(&*format!("to be able to read {value:?}"));
|
||||
|
||||
let mut keys: HashMap<String, String> = HashMap::new();
|
||||
|
||||
let re = Regex::new(r#"^(?:export\s+)?([^=]+)\s*=\s*(.+)$"#)
|
||||
.expect("Regex to be valid");
|
||||
|
||||
let _ = contents.split("\n")
|
||||
.filter_map(|line| re.captures(line))
|
||||
.map(|capture| (capture[0].to_owned(), capture[1].to_owned()))
|
||||
.map(|(key, value)| keys.insert(key, value));
|
||||
|
||||
Self(keys)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the contents of the file at the path specified by the given environment variable.
|
||||
pub fn get(dotenv: &DotEnv, key: &str) -> Option<String>
|
||||
{
|
||||
dotenv.var(key).map(|v| v.to_owned())
|
||||
}
|
21
src/sources/envfiles.rs
Normal file
21
src/sources/envfiles.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
//! Contents of files at paths defined by environment variables.
|
||||
|
||||
/// Get the contents of the file at the path specified by the given environment variable.
|
||||
pub fn get<Key>(key: Key) -> Option<String>
|
||||
where Key: AsRef<std::ffi::OsStr>,
|
||||
{
|
||||
let path = std::env::var(key).ok()?;
|
||||
|
||||
let path = std::ffi::OsString::from(path);
|
||||
let path = std::path::PathBuf::from(path);
|
||||
|
||||
let mut file = std::fs::File::open(&path)
|
||||
.expect(&*format!("to be able to open file at {path:?}"));
|
||||
|
||||
use std::io::Read;
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data)
|
||||
.expect(&*format!("to be able to read from file at {path:?}"));
|
||||
|
||||
Some(data)
|
||||
}
|
8
src/sources/envvars.rs
Normal file
8
src/sources/envvars.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
//! Environment variables.
|
||||
|
||||
/// Get the specified environment variable.
|
||||
pub fn get<Key>(key: Key) -> Option<String>
|
||||
where Key: AsRef<std::ffi::OsStr>,
|
||||
{
|
||||
std::env::var(key).ok()
|
||||
}
|
8
src/sources/mod.rs
Normal file
8
src/sources/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#[cfg(feature = "envvars")]
|
||||
pub mod envvars;
|
||||
|
||||
#[cfg(feature = "envfiles")]
|
||||
pub mod envfiles;
|
||||
|
||||
#[cfg(feature = "envdot")]
|
||||
pub mod envdot;
|
Loading…
Reference in a new issue