From c6df6cb3af94c318a34b89cc34019ba330c8dae4 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Fri, 29 Dec 2023 10:14:52 +0100 Subject: [PATCH] Clear the crate and start over --- examples/e01_the_cave/Cargo.toml | 8 - examples/e01_the_cave/src/main.rs | 9 - examples/e02_quick_math/Cargo.toml | 8 - examples/e02_quick_math/src/main.rs | 52 ----- examples/e03_order_a_pizza/Cargo.toml | 8 - examples/e03_order_a_pizza/src/main.rs | 119 ---------- src/handle/mod.rs | 153 ------------- src/lib.rs | 52 ----- src/macros/mod.rs | 82 ------- src/multi/mod.rs | 293 ------------------------- src/single/envfiles.rs | 137 ------------ src/single/envvars.rs | 99 --------- src/single/mod.rs | 15 -- src/testing/mod.rs | 17 -- tests/integration/macros.rs | 17 -- tests/integration/mod.rs | 1 - 16 files changed, 1070 deletions(-) delete mode 100644 examples/e01_the_cave/Cargo.toml delete mode 100644 examples/e01_the_cave/src/main.rs delete mode 100644 examples/e02_quick_math/Cargo.toml delete mode 100644 examples/e02_quick_math/src/main.rs delete mode 100644 examples/e03_order_a_pizza/Cargo.toml delete mode 100644 examples/e03_order_a_pizza/src/main.rs delete mode 100644 src/handle/mod.rs delete mode 100644 src/lib.rs delete mode 100644 src/macros/mod.rs delete mode 100644 src/multi/mod.rs delete mode 100644 src/single/envfiles.rs delete mode 100644 src/single/envvars.rs delete mode 100644 src/single/mod.rs delete mode 100644 src/testing/mod.rs delete mode 100644 tests/integration/macros.rs delete mode 100644 tests/integration/mod.rs diff --git a/examples/e01_the_cave/Cargo.toml b/examples/e01_the_cave/Cargo.toml deleted file mode 100644 index c0517e0..0000000 --- a/examples/e01_the_cave/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "e01_the_cave" -description = "Echoes back the value of ECHO." -version = "0.0.0" -edition = "2021" - -[dependencies] -micronfig = { path = "../.." } diff --git a/examples/e01_the_cave/src/main.rs b/examples/e01_the_cave/src/main.rs deleted file mode 100644 index b7e4f00..0000000 --- a/examples/e01_the_cave/src/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::fmt::Display; - - -micronfig::required!(ECHO, String); - - -fn main() { - println!("{}", *ECHO); -} diff --git a/examples/e02_quick_math/Cargo.toml b/examples/e02_quick_math/Cargo.toml deleted file mode 100644 index aa06a52..0000000 --- a/examples/e02_quick_math/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "e02_quick_math" -description = "Performs OPERATOR between FIRST and SECOND." -version = "0.0.0" -edition = "2021" - -[dependencies] -micronfig = { path = "../.." } diff --git a/examples/e02_quick_math/src/main.rs b/examples/e02_quick_math/src/main.rs deleted file mode 100644 index 3784921..0000000 --- a/examples/e02_quick_math/src/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::fmt::{Display, Formatter}; -use std::str::FromStr; - - -micronfig::required!(FIRST, u64); -micronfig::required!(SECOND, u64); -micronfig::required!(OPERATOR, Operator); - - -fn main() { - let result = match *OPERATOR { - Operator::Sum => (*FIRST) + (*SECOND), - Operator::Subtraction => (*FIRST) - (*SECOND), - Operator::Multiplication => (*FIRST) * (*SECOND), - Operator::Division => (*FIRST) / (*SECOND), - }; - - println!("{} {} {} = {}", *FIRST, *OPERATOR, *SECOND, result) -} - - -pub enum Operator { - Sum, - Subtraction, - Multiplication, - Division, -} - -impl FromStr for Operator { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "+" => Ok(Self::Sum), - "-" => Ok(Self::Subtraction), - "*" => Ok(Self::Multiplication), - "/" => Ok(Self::Division), - _ => Err(()) - } - } -} - -impl Display for Operator { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", match self { - Self::Sum => "+", - Self::Subtraction => "-", - Self::Multiplication => "*", - Self::Division => "/", - }) - } -} \ No newline at end of file diff --git a/examples/e03_order_a_pizza/Cargo.toml b/examples/e03_order_a_pizza/Cargo.toml deleted file mode 100644 index 3c1211b..0000000 --- a/examples/e03_order_a_pizza/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "e03_order_a_pizza" -description = "Order a pizza using micronfig!" -version = "0.0.0" -edition = "2021" - -[dependencies] -micronfig = { path = "../.." } diff --git a/examples/e03_order_a_pizza/src/main.rs b/examples/e03_order_a_pizza/src/main.rs deleted file mode 100644 index c3a3122..0000000 --- a/examples/e03_order_a_pizza/src/main.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::fmt::Formatter; -use std::net::IpAddr; -use std::str::FromStr; - - -// The name of the person who ordered the pizza. -micronfig::required!(FULLNAME, String); - -// The (IP) address the pizza should be delivered to. -micronfig::required!(DESTINATION, IpAddr); - -// The base of the pizza to add toppings on. -micronfig::required!(PIZZABASE, PizzaBase); - -// The toppings to add to the pizza. -micronfig::optional!(PIZZATOPPINGS, PizzaToppingsList); -// A pizza with no toppings, to use as fallback. -const PIZZATOPPINGS_NONE: PizzaToppingsList = PizzaToppingsList{ list: vec![] }; - - -fn main() { - // Let's print the order! - println!("Pizza Order"); - println!("==========="); - println!(); - println!("Base:"); - println!("- {}", *PIZZABASE); - println!(); - println!("Toppings:"); - for topping in &(*PIZZATOPPINGS).as_ref().unwrap_or(&PIZZATOPPINGS_NONE).list { - println!("- {}", &topping); - }; - println!(); - println!("Deliver to:"); - println!("{} @ {}", *FULLNAME, *DESTINATION) -} - - -/// A possible base of pizza. -#[derive(Clone, Copy, Debug)] -enum PizzaBase { - /// Just the pizza dough, with nothing else on top f it. - Blank, - /// Pizza dough with tomato on top. - Red, - /// Pizza dough with mozzarella on top. - White, - /// Pizza dough with both tomato and mozzarella on top. - Margherita, -} - -impl FromStr for PizzaBase { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - // Italian - "vuota" => Ok(Self::Blank), - "stria" => Ok(Self::Blank), - "rossa" => Ok(Self::Red), - "marinara" => Ok(Self::Red), - "pomodoro" => Ok(Self::Red), - "bianca" => Ok(Self::White), - "mozzarella" => Ok(Self::White), - "regina" => Ok(Self::Margherita), - "margherita" => Ok(Self::Margherita), - "normale" => Ok(Self::Margherita), - "entrambi" => Ok(Self::Margherita), - // English - "blank" => Ok(Self::Blank), - "red" => Ok(Self::Red), - "tomato" => Ok(Self::Red), - "white" => Ok(Self::White), - "cheese" => Ok(Self::White), - "both" => Ok(Self::Margherita), - "normal" => Ok(Self::Margherita), - // Unknown - _ => Err("Unknown pizza base; ensure you have written the name in either English or Italian!"), - } - } -} - -impl std::fmt::Display for PizzaBase { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", match self { - PizzaBase::Blank => "Blank (Empty)", - PizzaBase::Red => "Red (Tomato)", - PizzaBase::White => "White (Mozzarella)", - PizzaBase::Margherita => "Margherita (Tomato + Mozzarella)" - }) - } -} - -/// The toppings -#[derive(Clone, Debug)] -struct PizzaToppingsList { - pub list: Vec -} - -impl FromStr for PizzaToppingsList { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - let list: Vec = s.split(",").map(|s| s.to_string()).collect(); - - for topping in list.iter() { - // Ensure compatibility with https://github.com/rust-lang/rust/pull/70645 - if ["pineapple", "ananas"].contains(&topping.as_str()) { - return Err("Ruining pizzas is not allowed by the Rust compiler.") - } - } - - Ok( - PizzaToppingsList { - list - } - ) - } -} \ No newline at end of file diff --git a/src/handle/mod.rs b/src/handle/mod.rs deleted file mode 100644 index db82c2a..0000000 --- a/src/handle/mod.rs +++ /dev/null @@ -1,153 +0,0 @@ -//! High-level API — Handle errors automatically. -//! -//! It can be useful if you want to specify when configuration values are loaded in the lifecycle of your binary. - - -/// Get a value from the first available source, panicking with a human-readable message in case the value is missing or cannot be processed. -/// -/// # Process -/// -/// This function: -/// -/// 1. calls [`crate::multi::get`] with the given key and a file suffix of `_FILE` -/// 2. pattern matches errors and [`panic`]s if an error is caught. -/// -/// # Panics -/// -/// Any error encountered by this function causes a panic with a message describing what went wrong. -/// -/// The same thing happens if the configuration value could not be retrieved by any source. -/// -/// # Examples -/// -/// Retrieve a configuration value from either the `USER` environment variable or the `USER_FILE` file, maintaining it as a [`String`]: -/// ``` -/// use micronfig::handle::get_required; -/// # -/// # std::env::set_var("USER", "steffo"); -/// # std::env::remove_var("USER_FILE"); -/// -/// let user: String = get_required("USER"); -/// ``` -/// -/// Retrieve a configuration value from the `IP_ADDRESS` environment variable or the `IP_ADDRESS_FILE` file, then try to convert it to a [`std::net::IpAddr`]: -/// ``` -/// use std::net::IpAddr; -/// use micronfig::handle::get_required; -/// # -/// # std::env::set_var("IP_ADDRESS", "192.168.1.1"); -/// # std::env::remove_var("IP_ADDRESS_FILE"); -/// -/// let ip_addr: IpAddr = get_required("IP_ADDRESS"); -/// ``` -/// -/// # See also -/// -/// [`get_optional`], which has the same behaviour but does not panic if the value is not found, instead returning [`None`]. -/// -/// # Possible future improvements -/// -/// Possibly refactor this to a method of [`crate::multi::Source`]. -/// -pub fn get_required(key: &str) -> Type - where Type: std::str::FromStr, - ::Err: std::fmt::Debug, -{ - use crate::multi::{get, Source}; - use crate::single::{envvars, envfiles}; - - match get(key, "_FILE") { - Source::EnvVar(Ok(v)) => v, - Source::EnvVar(Err(envvars::Error::CannotConvertValue(err))) => - panic!("The contents of the {} environment variable could not be converted to a {}: {:?}", &key, &std::any::type_name::(), &err), - Source::EnvVar(Err(envvars::Error::CannotReadEnvVar(_))) => - panic!("Something unexpected happened in micronfig. Please report this as a bug!"), - - Source::EnvFile(Ok(v)) => v, - Source::EnvFile(Err(envfiles::Error::CannotConvertValue(err))) => - panic!("The contents of the file at {} could not be converted to a {}: {:?}", &key, &std::any::type_name::(), &err), - Source::EnvFile(Err(envfiles::Error::CannotOpenFile(err))) => - panic!("The file at {} could not be opened: {}", &key, &err), - Source::EnvFile(Err(envfiles::Error::CannotReadFile(err))) => - panic!("The contents of the file at {} could not be read: {}", &key, &err), - Source::EnvFile(Err(envfiles::Error::CannotReadEnvVar(_))) => - panic!("Something unexpected happened in micronfig. Please report this as a bug!"), - - Source::NotFound => - panic!("The configuration value {} is not defined.", &key), - } -} - - -/// Try to get a value from the first available source, panicking with a human-readable message in case it cannot be processed. -/// -/// # Process -/// -/// This function: -/// -/// 1. calls [`crate::multi::get`] with the given key and a file suffix of `_FILE` -/// 2. pattern matches errors and [`panic`]s if an error is caught. -/// -/// # Panics -/// -/// Any error encountered by this function causes a panic with a message describing what went wrong. -/// -/// # Examples -/// -/// Retrieve a configuration value from either the `USER` environment variable or the `USER_FILE` file, maintaining it as a [`String`]: -/// ``` -/// use micronfig::handle::get_optional; -/// # -/// # std::env::set_var("USER", "steffo"); -/// # std::env::remove_var("USER_FILE"); -/// -/// let user: Option = get_optional("USER"); -/// ``` -/// -/// Retrieve a configuration value from the `IP_ADDRESS` environment variable or the `IP_ADDRESS_FILE` file, then try to convert it to a [`std::net::IpAddr`]: -/// ``` -/// use std::net::IpAddr; -/// use micronfig::handle::get_optional; -/// # -/// # std::env::set_var("IP_ADDRESS", "192.168.1.1"); -/// # std::env::remove_var("IP_ADDRESS_FILE"); -/// -/// let ip_addr: Option = get_optional("IP_ADDRESS"); -/// ``` -/// -/// # See also -/// -/// [`get_required`], which has the same behaviour but does panics if the value is not found. -/// -/// # Possible future improvements -/// -/// Possibly refactor this to a method of [`crate::multi::Source`]. -/// -pub fn get_optional(name: &str) -> Option - where Type: std::str::FromStr, - ::Err: std::fmt::Debug, -{ - use crate::multi::{get, Source}; - use crate::single::{envvars, envfiles}; - - match get(name, "_FILE") { - Source::EnvVar(Ok(v)) => Some(v), - Source::EnvVar(Err(envvars::Error::CannotConvertValue(err))) => - panic!("The contents of the {} environment variable could not be converted to a {}: {:?}", &name, &std::any::type_name::(), &err), - Source::EnvVar(Err(envvars::Error::CannotReadEnvVar(_))) => - panic!("Something unexpected happened in micronfig. Please report this as a bug!"), - - Source::EnvFile(Ok(v)) => Some(v), - Source::EnvFile(Err(envfiles::Error::CannotConvertValue(err))) => - panic!("The contents of the file at {} could not be converted to a {}: {:?}", &name, &std::any::type_name::(), &err), - Source::EnvFile(Err(envfiles::Error::CannotOpenFile(err))) => - panic!("The file at {} could not be opened: {}", &name, &err), - Source::EnvFile(Err(envfiles::Error::CannotReadFile(err))) => - panic!("The contents of the file at {} could not be read: {}", &name, &err), - Source::EnvFile(Err(envfiles::Error::CannotReadEnvVar(_))) => - panic!("Something unexpected happened in micronfig. Please report this as a bug!"), - - Source::NotFound => None, - - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index dd1267d..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! Tiny crate for simple configuration management. -//! -//! > **Unstable**; I haven't fully committed to the API yet, so it might change wildly in the following minor versions (`0.x.0`). -//! -//! # Features -//! -//! This crate handles: -//! -//! - Retrieval of values of configuration properties from multiple sources, such as environment variables or files -//! - Parsing of retrieved data -//! - Displaying human-readable errors if case a step does not succeed -//! -//! # Usage -//! -//! This crate has four levels of abstraction, each one with a different usage method. -//! -//! In order from the highest to the lowest, they are: -//! -//! 1. **Recommended**: [`required`] and [`optional`], macros which allow you to define global, lazily-evaluated, configuration values; -//! 2. [`handle::get_required`] and [`handle::get_optional`], functions which allow you to get a configuration value in a specific moment, without having to consider handling errors; -//! 3. [`multi::get`], function which behaves in the same way as the previous two, but returns [`multi::Source`] instead, allowing you to handle errors how you prefer; -//! 4. [`single`], module containing submodules allowing the retrieval of configuration values from a single source, returning a source-specific [`Result`]. -//! -//! ## Examples -//! -//! Some examples are provided in the crate source, [inside the `examples/` directory](https://github.com/Steffo99/micronfig/tree/main/examples). - -#![warn(missing_docs)] -#![doc(html_logo_url = "https://raw.githubusercontent.com/Steffo99/micronfig/main/icon.png")] -#![cfg_attr(docsrs, feature(doc_cfg))] - - -pub mod single; - -#[cfg(feature = "multi")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "multi")))] -pub mod multi; - -#[cfg(feature = "handle")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "handle")))] -pub mod handle; - -#[cfg(feature = "macros")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))] -pub use lazy_static; -#[cfg(feature = "macros")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))] -pub mod macros; - -#[cfg(feature = "testing")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "testing")))] -pub mod testing; \ No newline at end of file diff --git a/src/macros/mod.rs b/src/macros/mod.rs deleted file mode 100644 index 74b8b03..0000000 --- a/src/macros/mod.rs +++ /dev/null @@ -1,82 +0,0 @@ -//! Highest-level API — Define lazy statics. -//! -//! The recommended way to use the library. - - -/// Define a required configuration value with a certain type. -/// -/// # Process -/// -/// This macro: -/// -/// 1. uses [`lazy_static::lazy_static`] to define a new static variable (and associated struct) -/// 2. uses [`crate::handle::get_required`] to get the configuration value and handle eventual errors -/// -/// # Examples -/// -/// Define a configuration value with the `USER` key, a [`String`]: -/// ``` -/// # std::env::set_var("USER", "steffo"); -/// # std::env::remove_var("USER_FILE"); -/// # -/// micronfig::required!(USER, String); -/// println!("{:?}", *USER); -/// ``` -/// -/// Retrieve a configuration value from the `IP_ADDRESS` environment variable or the `IP_ADDRESS_FILE` file, then try to convert it to a [`std::net::IpAddr`]: -/// ``` -/// use std::net::IpAddr; -/// # -/// # std::env::set_var("IP_ADDRESS", "192.168.1.1"); -/// # std::env::remove_var("IP_ADDRESS_FILE"); -/// -/// micronfig::required!(IP_ADDRESS, IpAddr); -/// println!("{:?}", *IP_ADDRESS); -/// ``` -#[macro_export] -macro_rules! required { - ($identifier:ident, $kind:ty) => { - $crate::lazy_static::lazy_static! { - pub(crate) static ref $identifier: $kind = $crate::handle::get_required::<$kind>(stringify!($identifier)); - } - }; -} - -/// Define a optional configuration value with a certain type. -/// -/// # Process -/// -/// This macro: -/// -/// 1. uses [`lazy_static::lazy_static`] to define a new static variable (and associated struct) -/// 2. uses [`crate::handle::get_optional`] to get the configuration value and handle eventual errors -/// -/// # Examples -/// -/// Define a configuration value with the `USER` key, a [`String`]: -/// ``` -/// # std::env::set_var("USER", "steffo"); -/// # std::env::remove_var("USER_FILE"); -/// # -/// micronfig::optional!(USER, String); -/// println!("{:?}", *USER); -/// ``` -/// -/// Retrieve a configuration value from the `IP_ADDRESS` environment variable or the `IP_ADDRESS_FILE` file, then try to convert it to a [`std::net::IpAddr`]: -/// ``` -/// use std::net::IpAddr; -/// # -/// # std::env::set_var("IP_ADDRESS", "192.168.1.1"); -/// # std::env::remove_var("IP_ADDRESS_FILE"); -/// -/// micronfig::optional!(IP_ADDRESS, IpAddr); -/// println!("{:?}", *IP_ADDRESS); -/// ``` -#[macro_export] -macro_rules! optional { - ($identifier:ident, $kind:ty) => { - $crate::lazy_static::lazy_static! { - pub(crate) static ref $identifier: Option<$kind> = $crate::handle::get_optional::<$kind>(stringify!($identifier)); - } - } -} diff --git a/src/multi/mod.rs b/src/multi/mod.rs deleted file mode 100644 index a7a0aca..0000000 --- a/src/multi/mod.rs +++ /dev/null @@ -1,293 +0,0 @@ -//! Middle-level API — Use all available configuration sources. -//! -//! It can be useful if you want more control on how errors are handled or on how the key is passed to the [`crate::single`] sources. - - -use std::ffi::OsString; -#[cfg(feature = "single_envvars")] use crate::single::envvars; -#[cfg(feature = "single_envfiles")] use crate::single::envfiles; - - -/// Get a value from the first available source, additionally returning information about how the value was retrieved. -/// -/// # Process -/// -/// This function tries to `get` a configuration value: -/// -/// 1. with [`envvars::get`], using `key`, returning a [`Source::EnvVar`] -/// 2. with [`envfiles::get`], using `key + key_suffix_file`, returning a [`Source::EnvFile`] -/// -/// If none of these options successfully resulted in the successful retrieval of the configuration value, [`Source::NotFound`] is returned instead. -/// -/// # Errors -/// -/// All errors encountered are bubbled up, except the ones surfacing because of the total absence of a configuration value, which make the function immediately try the next available source. -/// -/// Currently, those errors are: -/// - [`envvars::Error::CannotReadEnvVar`] -/// - [`envfiles::Error::CannotReadEnvVar`] -/// -/// # Examples -/// -/// Retrieve a configuration value from either the `USER` environment variable or the `USER_FILE` file, maintaining it as a [`String`]: -/// ``` -/// use micronfig::multi::get; -/// use micronfig::multi::Source; -/// # -/// # std::env::set_var("USER", "steffo"); -/// # std::env::remove_var("USER_FILE"); -/// -/// let user: Source = get("USER", "_FILE"); -/// ``` -/// -/// Retrieve a configuration value from the `IP_ADDRESS` environment variable or the `IP_ADDRESS_FILE` file, then try to convert it to a [`std::net::IpAddr`]: -/// ``` -/// use std::net::IpAddr; -/// use micronfig::multi::get; -/// use micronfig::multi::Source; -/// # -/// # std::env::set_var("IP_ADDRESS", "192.168.1.1"); -/// # std::env::remove_var("IP_ADDRESS_FILE"); -/// -/// let ip_addr: Source = get("IP_ADDRESS", "_FILE"); -/// ``` -/// -pub fn get(key: Key, key_suffix_file: KeySuffixFile) -> Source - where Key: AsRef, - KeySuffixFile: AsRef, - Type: std::str::FromStr, - ::Err: std::fmt::Debug, -{ - - if cfg!(feature = "single_envvars") { - let v = envvars::get(&key); - - match v { - Err(envvars::Error::CannotReadEnvVar(_)) => {}, - _ => return Source::EnvVar(v), - } - } - - if cfg!(feature = "single_envfiles") { - let mut key_file = OsString::new(); - key_file.push(key); - key_file.push(key_suffix_file); - let v = envfiles::get(key_file); - - match v { - Err(envfiles::Error::CannotReadEnvVar(_)) => {}, - _ => return Source::EnvFile(v), - } - } - - Source::NotFound -} - - -/// The way the result returned by [`get`] was obtained. -/// -/// Since more sources might be added in the future, this function is `non_exaustive`. -#[non_exhaustive] -#[derive(Debug)] -pub enum Source - where Type: std::str::FromStr, - ::Err: std::fmt::Debug, -{ - /// The result was not obtained, since the configuration value was not defined anywhere. - NotFound, - - /// The result was obtained by [`envvars::get`]. - #[cfg(feature = "single_envvars")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "single_envvars")))] - EnvVar(envvars::Result), - - /// The result was obtained by [`envfiles::get`]. - #[cfg(feature = "single_envfiles")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "single_envfiles")))] - EnvFile(envfiles::Result), -} - -impl Source - where Type: std::str::FromStr, - ::Err: std::fmt::Debug, -{ - /// Returns any contained [`Ok`] value, consuming both `self` and the [`Source`] inside. - /// - /// # Panics - /// - /// This function panics if `self` is a [`Source::NotFound`], or if the contained value is a [`Err`]. - /// - /// The panic message is the `msg` given. - /// - /// # See also - /// - /// Similar to [`Result::expect`]. - /// - /// Used by [`Self::unwrap`]. - /// - /// # Examples - /// - /// ``` - /// use micronfig::multi::Source; - /// - /// let value = Source::::EnvFile(Ok(1)).expect("value to be present"); - /// assert_eq!(value, 1) - /// ``` - /// - /// ```should_panic - /// use micronfig::multi::Source; - /// use micronfig::single::envfiles::Error as FileError; - /// - /// let value = Source::::EnvFile(Err(FileError::CannotReadEnvVar(std::env::VarError::NotPresent))).expect("value to be present"); - /// // Panic! - /// ``` - pub fn expect(self, msg: &str) -> Type { - match self { - #[cfg(feature = "single_envvars")] - Self::EnvVar(Ok(v)) => v, - - #[cfg(feature = "single_envfiles")] - Self::EnvFile(Ok(v)) => v, - - _ => panic!("{}", msg), - } - } - - /// Returns any contained [`Ok`] value, consuming both `self` and the [`Source`] inside. - /// - /// # Panics - /// - /// This function panics if `self` is a [`Source::NotFound`], or if the contained value is a [`Err`]. - /// - /// # See also - /// - /// Similar to [`Result::unwrap`]. - /// - /// Internally, it uses [`Self::expect`]. - /// - /// # Examples - /// - /// ``` - /// use micronfig::multi::Source; - /// - /// let value = Source::::EnvFile(Ok(1)).unwrap(); - /// assert_eq!(value, 1) - /// ``` - /// - /// ```should_panic - /// use micronfig::multi::Source; - /// use micronfig::single::envfiles::Error as FileError; - /// - /// let value = Source::::EnvFile(Err(FileError::CannotReadEnvVar(std::env::VarError::NotPresent))).unwrap(); - /// // Panic! - /// ``` - pub fn unwrap(self) -> Type - { - self.expect("called `Source::unwrap()` on an invalid variant, such as `NotFound` or `_(Err(_))`") - } -} - - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::testing::tempfile_fixture; - - #[test] - #[cfg(feature = "single_envvars")] - fn it_works_var() { - std::env::set_var("NUMBER", "1"); - std::env::remove_var("NUMBER_FILE"); - - match get::<&str, &str, u32>("NUMBER", "_FILE") { - Source::EnvVar(Ok(1u32)) => {}, - _ => panic!("expected Source::EnvVar(Ok(1u32))") - } - } - - #[test] - #[cfg(feature = "single_envfiles")] - fn it_works_file() { - let file = tempfile_fixture("1"); - std::env::remove_var("NUMBER"); - std::env::set_var("NUMBER_FILE", file.as_os_str()); - - let n = get::<&str, &str, u32>("NUMBER", "_FILE"); - match n { - Source::EnvFile(Ok(1u32)) => {}, - _ => panic!("expected Source::EnvFile(Ok(1u32))") - } - } - - #[test] - #[cfg(feature = "single_envvars")] - fn missing_envvar() { - match get::<&str, &str, String>("MISSING_ENVVAR", "_FILE") { - Source::NotFound => {}, - _ => panic!("expected Source::NotFound"), - } - } - - #[test] - #[cfg(feature = "single_envfiles")] - fn missing_file() { - std::env::remove_var("NUMBER"); - std::env::set_var("NUMBER_FILE", "/this/file/does/not/exist"); - - match get::<&str, &str, u32>("NUMBER", "_FILE") { - Source::EnvFile(Err(envfiles::Error::CannotOpenFile(_))) => {}, - _ => panic!("expected Source::EnvFile(Err(envfiles::Error::CannotOpenFile(_)))"), - } - } - - #[test] - #[cfg(feature = "single_envvars")] - fn not_a_number_var() { - std::env::set_var("NUMBER", "XYZ"); - std::env::remove_var("NUMBER_FILE"); - - match get::<&str, &str, u32>("NUMBER", "_FILE") { - Source::EnvVar(Err(envvars::Error::CannotConvertValue(_))) => {}, - _ => panic!("expected Source::EnvVar(Err(envvars::Error::CannotConvertValue(_)))"), - } - } - - #[test] - #[cfg(feature = "single_envfiles")] - fn not_a_number_file() { - let file = tempfile_fixture("XYZ"); - std::env::set_var("NUMBER_FILE", file.as_os_str()); - std::env::remove_var("NUMBER"); - - match get::<&str, &str, u32>("NUMBER", "_FILE") { - Source::EnvFile(Err(envfiles::Error::CannotConvertValue(_))) => {}, - _ => panic!("expected Source::EnvFile(Err(envfiles::Error::CannotConvertValue(_)))"), - } - } - - #[test] - #[cfg(feature = "single_envvars")] - fn unwrap_var_ok() { - Source::EnvVar(Ok("ok".to_string())).unwrap(); - } - - #[test] - #[cfg(feature = "single_envfiles")] - fn unwrap_file_ok() { - Source::EnvFile(Ok("ok".to_string())).unwrap(); - } - - #[test] - #[should_panic] - #[cfg(feature = "single_envvars")] - fn unwrap_var_err() { - Source::::EnvVar(Err(envvars::Error::CannotReadEnvVar(std::env::VarError::NotPresent))).unwrap(); - } - - #[test] - #[should_panic] - #[cfg(feature = "single_envfiles")] - fn unwrap_file_err() { - Source::::EnvFile(Err(envfiles::Error::CannotReadEnvVar(std::env::VarError::NotPresent))).unwrap(); - } -} \ No newline at end of file diff --git a/src/single/envfiles.rs b/src/single/envfiles.rs deleted file mode 100644 index 279e897..0000000 --- a/src/single/envfiles.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! Contents of files at paths defined by environment variables. - - -/// Get a configuration value from the source. -/// -/// # Process -/// -/// This function: -/// -/// 1. tries to access the environment variable with the given name using [`std::env::var`] -/// 2. tries to interpret the contents of the environment variable as a [`std::path::PathBuf`] -/// 3. tries to [`std::fs::File::open`] the file at that path -/// 4. tries to [`std::io::Read::read_to_string`] the contents of the opened file -/// 5. tries to convert the obtained value to another of the given type using [`std::str::FromStr::from_str`] -/// -/// # Examples -/// -/// Retrieve a configuration value from the `USER_FILE` file, maintaining it as a [`String`]: -/// ``` -/// use micronfig::single::envfiles::get; -/// -/// # let filename = micronfig::testing::tempfile_fixture("steffo"); -/// # std::env::set_var("USER_FILE", filename.as_os_str()); -/// let user: String = get("USER_FILE").expect("USER_FILE envvar to be defined"); -/// ``` -/// -/// Retrieve a configuration value from the `IP_ADDRESS_FILE` file, then try to convert it to a [`std::net::IpAddr`]: -/// ``` -/// use std::net::IpAddr; -/// use micronfig::single::envfiles::get; -/// -/// # let filename = micronfig::testing::tempfile_fixture("192.168.1.1"); -/// # std::env::set_var("IP_ADDRESS_FILE", filename.as_os_str()); -/// let ip_addr: IpAddr = get("IP_ADDRESS_FILE").expect("IP_ADDRESS_FILE envvar to be defined"); -/// ``` -/// -pub fn get(key: Key) -> Result - where Key: AsRef, - Type: std::str::FromStr, - ::Err: std::fmt::Debug, -{ - let path = std::env::var(key) - .map_err(Error::CannotReadEnvVar)?; - let path = std::ffi::OsString::from(path); - let path = std::path::PathBuf::from(path); - - let mut file = std::fs::File::open(path) - .map_err(Error::CannotOpenFile)?; - - use std::io::Read; - let mut data = String::new(); - file.read_to_string(&mut data) - .map_err(Error::CannotReadFile)?; - - let value = Type::from_str(&data) - .map_err(Error::CannotConvertValue)?; - - Ok(value) -} - - -/// A possible error encountered by [`get`]. -#[derive(std::fmt::Debug)] -pub enum Error - where ConversionError: std::fmt::Debug, -{ - /// The environment variable could not be read. - /// - /// Encountered when the call to [`std::env::var`] fails. - CannotReadEnvVar(std::env::VarError), - - /// The specified file could not be opened. (Probably it doesn't exist.) - /// - /// Encountered when the call to [`std::fs::File::open`] fails. - CannotOpenFile(std::io::Error), - - /// The specified file could not be read. - /// - /// Encountered when the call to [`std::io::Read::read_to_string`] fails. - CannotReadFile(std::io::Error), - - /// The value could not be converted to the desired type. - /// - /// Encountered when the call to [`std::str::FromStr::from_str`] fails. - CannotConvertValue(ConversionError), -} - - -/// A possible error encountered by [`get`]. -pub type Result = std::result::Result::Err>>; - - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::testing::tempfile_fixture; - - #[test] - fn it_works() { - let file = tempfile_fixture("1"); - std::env::set_var("NUMBER_FILE", file.as_os_str()); - - let number = get::<&str, u32>("NUMBER_FILE").unwrap(); - assert_eq!(number, 1u32); - } - - #[test] - fn missing_envvar() { - std::env::remove_var("THIS_ENVVAR_DOES_NOT_EXIST_FILE"); - - match get::<&str, String>("THIS_ENVVAR_DOES_NOT_EXIST_FILE") { - Err(Error::CannotReadEnvVar(std::env::VarError::NotPresent)) => {}, - _ => panic!("expected Err(Error::CannotReadEnvVar(std::env::VarError::NotPresent))"), - } - } - - #[test] - fn missing_file() { - std::env::set_var("NUMBER_FILE", "/this/file/does/not/exist"); - - match get::<&str, u32>("NUMBER_FILE") { - Err(Error::CannotOpenFile(_)) => {}, - _ => panic!("expected Err(Error::CannotOpenFile(_))"), - } - } - - #[test] - fn not_a_number() { - let file = tempfile_fixture("XYZ"); - std::env::set_var("NUMBER_FILE", file.as_os_str()); - - match get::<&str, u32>("NUMBER_FILE") { - Err(Error::CannotConvertValue(_)) => {}, - _ => panic!("expected Err(Error::CannotConvertValue(_))"), - } - } -} \ No newline at end of file diff --git a/src/single/envvars.rs b/src/single/envvars.rs deleted file mode 100644 index aab7eb8..0000000 --- a/src/single/envvars.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Environment variables. - - -/// Get a configuration value from the source. -/// -/// # Process -/// -/// This function: -/// -/// 1. tries to access the environment variable with the given name using [`std::env::var`] -/// 2. tries to convert the obtained value to another of the given type using [`std::str::FromStr::from_str`] -/// -/// # Examples -/// -/// Retrieve a configuration value from the `USER` environment variable, maintaining it as a [`String`]: -/// ``` -/// use micronfig::single::envvars::get; -/// -/// # std::env::set_var("USER", "steffo"); -/// let user: String = get("USER").expect("USER envvar to be defined"); -/// ``` -/// -/// Retrieve a configuration value from the `IP_ADDRESS` environment variable, then try to convert it to a [`std::net::IpAddr`]: -/// ``` -/// use std::net::IpAddr; -/// use micronfig::single::envvars::get; -/// -/// # std::env::set_var("IP_ADDRESS", "192.168.1.1"); -/// let ip_addr: IpAddr = get("IP_ADDRESS").expect("IP_ADDRESS envvar to be defined"); -/// ``` -/// -pub fn get(key: Key) -> Result - where Key: AsRef, - Type: std::str::FromStr, - ::Err: std::fmt::Debug, -{ - let data = std::env::var(key) - .map_err(Error::CannotReadEnvVar)?; - - let value = Type::from_str(&data) - .map_err(Error::CannotConvertValue)?; - - Ok(value) -} - - -/// A possible error encountered by [`get`]. -#[derive(std::fmt::Debug)] -pub enum Error - where ConversionError: std::fmt::Debug, -{ - /// The environment variable could not be read. - /// - /// Encountered when the call to [`std::env::var`] fails. - CannotReadEnvVar(std::env::VarError), - - /// The value could not be converted to the desired type. - /// - /// Encountered when the call to [`std::str::FromStr::from_str`] fails. - CannotConvertValue(ConversionError), -} - - -/// The result of [`get`]. -pub type Result = std::result::Result::Err>>; - - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - - #[test] - fn it_works() { - std::env::set_var("NUMBER", "1"); - - let number = get::<&str, u32>("NUMBER").unwrap(); - assert_eq!(number, 1u32); - } - - #[test] - fn missing_envvar() { - std::env::remove_var("THIS_ENVVAR_DOES_NOT_EXIST"); - - match get::<&str, String>("THIS_ENVVAR_DOES_NOT_EXIST") { - Err(Error::CannotReadEnvVar(std::env::VarError::NotPresent)) => {}, - _ => panic!("expected Err(Error::CannotReadEnvVar(std::env::VarError::NotPresent))"), - } - } - - #[test] - fn not_a_number() { - std::env::set_var("NUMBER", "XYZ"); - - match get::<&str, u32>("NUMBER") { - Err(Error::CannotConvertValue(_)) => {}, - _ => panic!("expected Error::CannotConvertValue(_)"), - } - } -} diff --git a/src/single/mod.rs b/src/single/mod.rs deleted file mode 100644 index 13ebf77..0000000 --- a/src/single/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Lowest-level API — Manually select configuration sources. -//! -//! It can be useful if you want to specify manually the sources to access when retrieving configuration values. -//! -//! Each possible source has an associated module, and a feature named `single_{MODULENAME}` enabling it; see the list of modules below to see what sources are available! - - -#[cfg(feature = "single_envvars")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "single_envvars")))] -pub mod envvars; - -#[cfg(feature = "single_envfiles")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "single_envfiles")))] -pub mod envfiles; - diff --git a/src/testing/mod.rs b/src/testing/mod.rs deleted file mode 100644 index 9a6b778..0000000 --- a/src/testing/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Fixtures for testing. -//! -//! **Unstable**; not supposed to be used outside this crate; do not add `pub(crate)` or doctests will stop working. - -/// Create a temporary file and write `content` inside it. -/// -/// The file will be deleted as soon as the [`tempfile::TempPath`] is dropped. -pub fn tempfile_fixture(content: &str) -> tempfile::TempPath { - use std::io::Write; - - let mut file = tempfile::NamedTempFile::new() - .expect("the tempfile fixture to be created successfully"); - write!(file, "{}", content) - .expect("to be able to write into the tempfile fixture"); - - file.into_temp_path() -} diff --git a/tests/integration/macros.rs b/tests/integration/macros.rs deleted file mode 100644 index 6dafe09..0000000 --- a/tests/integration/macros.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::env; - -micronfig::required!(PLAYER_NAME, String); -micronfig::required!(PLAYER_ID, u64); -micronfig::optional!(IS_SUS, bool); - - -#[test] -fn test_macros() { - env::set_var("PLAYER_NAME", "Steffo"); - env::set_var("PLAYER_ID", "1234"); - env::remove_var("IS_SUS"); - - assert_eq!(*PLAYER_NAME, "Steffo"); - assert_eq!(*PLAYER_ID, 1234u64); - assert_eq!(*IS_SUS, None); -} diff --git a/tests/integration/mod.rs b/tests/integration/mod.rs deleted file mode 100644 index eda363d..0000000 --- a/tests/integration/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod macros;