mirror of
https://github.com/Steffo99/micronfig.git
synced 2024-11-24 09:04:18 +00:00
Document and refine the public API
This commit is contained in:
parent
e081fc693c
commit
6c955c508f
4 changed files with 161 additions and 40 deletions
47
src/any.rs
47
src/any.rs
|
@ -1,19 +1,26 @@
|
|||
//! Module defining the general [`get`] low-level function and its associated [`Source`] type.
|
||||
//! Module defining the general [`value`] high-level function, the general [`get`] low-level function, and its associated [`Source`] type.
|
||||
|
||||
use std::ffi::OsString;
|
||||
use crate::var;
|
||||
use crate::file;
|
||||
|
||||
|
||||
/// Get a configuration value, maintaining information about how the value was retrieved.
|
||||
/// Get a configuration value from the first available source and convert it to the given `Type`, additionally returning information about how the value was retrieved.
|
||||
///
|
||||
/// # Process
|
||||
///
|
||||
/// This function tries to get a configuration value:
|
||||
///
|
||||
/// 1. with [`var::get`] using `name_var`, returning a [`Source::Var`]
|
||||
/// 2. with [`file::get`] using `name_file`, returning a [`Source::File`]
|
||||
/// 1. with [`var::get`] using `name`, returning a [`Source::Var`]
|
||||
/// 2. with [`file::get`] using `name + file_suffix`, returning a [`Source::File`]
|
||||
///
|
||||
/// If none of these options successfully resulted in the successful retrieval of the configuration value, [`Source::NotFound`] is returned.
|
||||
/// If none of these options successfully resulted in the successful retrieval of the configuration value, [`Source::NotFound`] is returned instead.
|
||||
///
|
||||
/// All errors are bubbled up, except the ones surfacing because of the total absence of a configuration value, currently:
|
||||
/// # Errors
|
||||
///
|
||||
/// All errors are bubbled up, except the ones surfacing because of the total absence of a configuration value, which make the function try the next available source.
|
||||
///
|
||||
/// Currently, those are:
|
||||
/// - [`var::Error::CannotReadEnvVar`]
|
||||
/// - [`file::Error::CannotReadEnvVar`]
|
||||
///
|
||||
|
@ -27,7 +34,7 @@ use crate::file;
|
|||
/// # std::env::set_var("NUMBER", "1");
|
||||
/// # std::env::remove_var("NUMBER_FILE");
|
||||
///
|
||||
/// let value = get::<&str, &str, u32>("NUMBER", "NUMBER_FILE");
|
||||
/// let value = get::<&str, &str, u32>("NUMBER", "_FILE");
|
||||
/// if let Source::Var(Ok(1)) = value {} else { panic!() }
|
||||
/// ```
|
||||
///
|
||||
|
@ -39,22 +46,26 @@ use crate::file;
|
|||
/// # std::env::remove_var("NUMBER");
|
||||
/// # std::env::remove_var("NUMBER_FILE");
|
||||
///
|
||||
/// let value = get::<&str, &str, u32>("NUMBER", "NUMBER_FILE");
|
||||
/// let value = get::<&str, &str, u32>("NUMBER", "_FILE");
|
||||
/// if let Source::NotFound = value {} else { panic!() }
|
||||
/// ```
|
||||
///
|
||||
pub fn get<KeyVar, KeyFile, Type>(name_var: KeyVar, name_file: KeyFile) -> Source<Type>
|
||||
pub fn get<KeyVar, KeyFile, Type>(name: KeyVar, file_suffix: KeyFile) -> Source<Type>
|
||||
where KeyVar: AsRef<std::ffi::OsStr>,
|
||||
KeyFile: AsRef<std::ffi::OsStr>,
|
||||
Type: std::str::FromStr,
|
||||
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
let v = var::get(name_var);
|
||||
let v = var::get(&name);
|
||||
|
||||
match v {
|
||||
Err(var::Error::CannotReadEnvVar(_)) => {},
|
||||
_ => return Source::Var(v),
|
||||
}
|
||||
|
||||
let mut name_file = OsString::new();
|
||||
name_file.push(name);
|
||||
name_file.push(file_suffix);
|
||||
let v = file::get(name_file);
|
||||
|
||||
match v {
|
||||
|
@ -72,6 +83,7 @@ pub fn get<KeyVar, KeyFile, Type>(name_var: KeyVar, name_file: KeyFile) -> Sourc
|
|||
#[non_exhaustive]
|
||||
pub enum Source<Type>
|
||||
where Type: std::str::FromStr,
|
||||
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
/// The result was not obtained, since the configuration value was not defined anywhere.
|
||||
NotFound,
|
||||
|
@ -85,6 +97,7 @@ pub enum Source<Type>
|
|||
|
||||
impl<Type> Source<Type>
|
||||
where Type: std::str::FromStr,
|
||||
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
/// Returns any contained [`Ok`] value, consuming both `self` and the [`Source`] inside.
|
||||
///
|
||||
|
@ -130,8 +143,6 @@ impl<Type> Source<Type>
|
|||
///
|
||||
/// 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::unwrap`].
|
||||
|
@ -171,7 +182,7 @@ pub(crate) mod tests {
|
|||
std::env::set_var("NUMBER", "1");
|
||||
std::env::remove_var("NUMBER_FILE");
|
||||
|
||||
match get::<&str, &str, u32>("NUMBER", "NUMBER_FILE") {
|
||||
match get::<&str, &str, u32>("NUMBER", "_FILE") {
|
||||
Source::Var(Ok(1u32)) => {},
|
||||
_ => panic!("expected Source::Var(Ok(1u32))")
|
||||
}
|
||||
|
@ -183,7 +194,7 @@ pub(crate) mod tests {
|
|||
std::env::remove_var("NUMBER");
|
||||
std::env::set_var("NUMBER_FILE", file.as_os_str());
|
||||
|
||||
let n = get::<&str, &str, u32>("NUMBER", "NUMBER_FILE");
|
||||
let n = get::<&str, &str, u32>("NUMBER", "_FILE");
|
||||
match n {
|
||||
Source::File(Ok(1u32)) => {},
|
||||
_ => panic!("expected Source::File(Ok(1u32))")
|
||||
|
@ -192,7 +203,7 @@ pub(crate) mod tests {
|
|||
|
||||
#[test]
|
||||
fn missing_envvar() {
|
||||
match get::<&str, &str, String>("MISSING_ENVVAR", "MISSING_ENVVAR_FILE") {
|
||||
match get::<&str, &str, String>("MISSING_ENVVAR", "_FILE") {
|
||||
Source::NotFound => {},
|
||||
_ => panic!("expected Source::NotFound"),
|
||||
}
|
||||
|
@ -203,7 +214,7 @@ pub(crate) mod tests {
|
|||
std::env::remove_var("NUMBER");
|
||||
std::env::set_var("NUMBER_FILE", "/this/file/does/not/exist");
|
||||
|
||||
match get::<&str, &str, u32>("NUMBER", "NUMBER_FILE") {
|
||||
match get::<&str, &str, u32>("NUMBER", "_FILE") {
|
||||
Source::File(Err(file::Error::CannotOpenFile(_))) => {},
|
||||
_ => panic!("expected Source::File(Err(file::Error::CannotOpenFile(_)))"),
|
||||
}
|
||||
|
@ -214,7 +225,7 @@ pub(crate) mod tests {
|
|||
std::env::set_var("NUMBER", "XYZ");
|
||||
std::env::remove_var("NUMBER_FILE");
|
||||
|
||||
match get::<&str, &str, u32>("NUMBER", "NUMBER_FILE") {
|
||||
match get::<&str, &str, u32>("NUMBER", "_FILE") {
|
||||
Source::Var(Err(var::Error::CannotConvertValue(_))) => {},
|
||||
_ => panic!("expected Source::Var(Err(var::Error::CannotConvertValue(_)))"),
|
||||
}
|
||||
|
@ -226,7 +237,7 @@ pub(crate) mod tests {
|
|||
std::env::set_var("NUMBER_FILE", file.as_os_str());
|
||||
std::env::remove_var("NUMBER");
|
||||
|
||||
match get::<&str, &str, u32>("NUMBER", "NUMBER_FILE") {
|
||||
match get::<&str, &str, u32>("NUMBER", "_FILE") {
|
||||
Source::File(Err(file::Error::CannotConvertValue(_))) => {},
|
||||
_ => panic!("expected Source::File(Err(file::Error::CannotConvertValue(_)))"),
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
pub fn get<Key, Type>(name: Key) -> Result<Type>
|
||||
where Key: AsRef<std::ffi::OsStr>,
|
||||
Type: std::str::FromStr,
|
||||
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
let path = std::env::var(name)
|
||||
.map_err(Error::CannotReadEnvVar)?;
|
||||
|
@ -27,8 +28,9 @@ pub fn get<Key, Type>(name: Key) -> Result<Type>
|
|||
|
||||
|
||||
/// A possible error encountered by [`get`].
|
||||
#[derive(Debug)]
|
||||
#[derive(std::fmt::Debug)]
|
||||
pub enum Error<ConversionError>
|
||||
where ConversionError: std::fmt::Debug,
|
||||
{
|
||||
/// The environment variable could not be read.
|
||||
///
|
||||
|
|
143
src/lib.rs
143
src/lib.rs
|
@ -8,30 +8,135 @@
|
|||
//!
|
||||
//! This crate handles:
|
||||
//!
|
||||
//! 1. Retrieval of configuration values from multiple sources ([`any::get`])
|
||||
//! 1. The environment ([`var::get`])
|
||||
//! 2. Files specified in the environment ([`file::get`])
|
||||
//! 2. Conversion to a value of an arbitrary type ([`std::str::FromStr`])
|
||||
//! 3. Displaying a operator-friendly error in case
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! The following example:
|
||||
//!
|
||||
//! 1. Tries to retrieve the value of the configuration value `THIS_ENVVAR_CONTAINS_ONE`
|
||||
//! 1. From the `THIS_ENVVAR_CONTAINS_ONE` environment variable
|
||||
//! 2. From the contents of the file specified in the `THIS_ENVVAR_CONTAINS_ONE_FILE` environment variable
|
||||
//! 2. It converts the value to a [`u8`]
|
||||
//! 3. Panics with a operator-friendly error if any of these steps failed
|
||||
//!
|
||||
//! ```
|
||||
//! todo!()
|
||||
//! ```
|
||||
//! 1. Retrieval of configuration values from multiple sources
|
||||
//! 1. The environment
|
||||
//! 2. Files specified in the environment
|
||||
//! 2. Conversion to a value of an arbitrary type
|
||||
//! 3. Displaying a operator-friendly error if case one of this steps did not succeed
|
||||
|
||||
pub mod any;
|
||||
pub mod var;
|
||||
pub mod file;
|
||||
|
||||
/// Get the configuration value with the given `name` and convert it to the given `Type`.
|
||||
///
|
||||
/// # 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
|
||||
///
|
||||
/// ```
|
||||
/// // The NUMBER envvar has been previously set to "1".
|
||||
/// # std::env::set_var("NUMBER", "1");
|
||||
/// # std::env::remove_var("NUMBER_FILE");
|
||||
///
|
||||
/// let value: u8 = micronfig::required("NUMBER");
|
||||
/// assert_eq!(value, 1u8);
|
||||
/// ```
|
||||
///
|
||||
/// ```should_panic
|
||||
/// // The NUMBER envvar has not been set.
|
||||
/// # std::env::remove_var("NUMBER");
|
||||
/// # std::env::remove_var("NUMBER_FILE");
|
||||
///
|
||||
/// let value: u8 = micronfig::required("NUMBER");
|
||||
/// // Panic: The configuration value NUMBER is not defined.
|
||||
/// ```
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// [`any::get`], the function called by this one to get the configuration value.
|
||||
///
|
||||
pub fn required<Type>(name: &str) -> Type
|
||||
where Type: std::str::FromStr,
|
||||
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
use crate::any::{get, Source};
|
||||
|
||||
match get(name, "_FILE") {
|
||||
Source::Var(Ok(v)) => v,
|
||||
Source::Var(Err(var::Error::CannotConvertValue(_))) =>
|
||||
panic!("The contents of the {} environment variable could not be converted to a {}.", &name, &std::any::type_name::<Type>()),
|
||||
Source::Var(Err(var::Error::CannotReadEnvVar(_))) =>
|
||||
panic!("Something unexpected happened in micronfig. Please report this as a bug!"),
|
||||
|
||||
Source::File(Ok(v)) => v,
|
||||
Source::File(Err(file::Error::CannotConvertValue(_))) =>
|
||||
panic!("The contents of the file at {} could not be converted to a {}.", &name, &std::any::type_name::<Type>()),
|
||||
Source::File(Err(file::Error::CannotOpenFile(err))) =>
|
||||
panic!("The file at {} could not be opened: {}", &name, &err),
|
||||
Source::File(Err(file::Error::CannotReadFile(err))) =>
|
||||
panic!("The contents of the file at {} could not be read: {}", &name, &err),
|
||||
Source::File(Err(file::Error::CannotReadEnvVar(_))) =>
|
||||
panic!("Something unexpected happened in micronfig. Please report this as a bug!"),
|
||||
|
||||
Source::NotFound =>
|
||||
panic!("The configuration value {} is not defined.", &name),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Get the configuration value with the given `name` and convert it to the given `Type`, if it was defined somewhere.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Any error encountered by this function causes a panic with a message describing what went wrong.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// // The NUMBER envvar has been previously set to "1".
|
||||
/// # std::env::set_var("NUMBER", "1");
|
||||
/// # std::env::remove_var("NUMBER_FILE");
|
||||
///
|
||||
/// let value: Option<u8> = micronfig::optional("NUMBER");
|
||||
/// assert_eq!(value, Some(1u8));
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // The NUMBER envvar has not been set.
|
||||
/// # std::env::remove_var("NUMBER");
|
||||
/// # std::env::remove_var("NUMBER_FILE");
|
||||
///
|
||||
/// let value: Option<u8> = micronfig::optional("NUMBER");
|
||||
/// assert_eq!(value, None);
|
||||
/// ```
|
||||
///
|
||||
/// # See also
|
||||
///
|
||||
/// [`any::get`], the function called by this one to get the configuration value.
|
||||
///
|
||||
pub fn optional<Type>(name: &str) -> Option<Type>
|
||||
where Type: std::str::FromStr,
|
||||
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
use crate::any::{get, Source};
|
||||
|
||||
match get(name, "_FILE") {
|
||||
Source::Var(Ok(v)) => Some(v),
|
||||
Source::Var(Err(var::Error::CannotConvertValue(_))) =>
|
||||
panic!("The contents of the {} environment variable could not be converted to a {}.", &name, &std::any::type_name::<Type>()),
|
||||
Source::Var(Err(var::Error::CannotReadEnvVar(_))) =>
|
||||
panic!("Something unexpected happened in micronfig. Please report this as a bug!"),
|
||||
|
||||
Source::File(Ok(v)) => Some(v),
|
||||
Source::File(Err(file::Error::CannotConvertValue(_))) =>
|
||||
panic!("The contents of the file at {} could not be converted to a {}.", &name, &std::any::type_name::<Type>()),
|
||||
Source::File(Err(file::Error::CannotOpenFile(err))) =>
|
||||
panic!("The file at {} could not be opened: {}", &name, &err),
|
||||
Source::File(Err(file::Error::CannotReadFile(err))) =>
|
||||
panic!("The contents of the file at {} could not be read: {}", &name, &err),
|
||||
Source::File(Err(file::Error::CannotReadEnvVar(_))) =>
|
||||
panic!("Something unexpected happened in micronfig. Please report this as a bug!"),
|
||||
|
||||
Source::NotFound => None,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
pub fn get<Key, Type>(name: Key) -> Result<Type>
|
||||
where Key: AsRef<std::ffi::OsStr>,
|
||||
Type: std::str::FromStr,
|
||||
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
{
|
||||
let data = std::env::var(name)
|
||||
.map_err(Error::CannotReadEnvVar)?;
|
||||
|
@ -17,8 +18,10 @@ pub fn get<Key, Type>(name: Key) -> Result<Type>
|
|||
|
||||
|
||||
/// A possible error encountered by [`get`].
|
||||
#[derive(Debug)]
|
||||
pub enum Error<ConversionError> {
|
||||
#[derive(std::fmt::Debug)]
|
||||
pub enum Error<ConversionError>
|
||||
where ConversionError: std::fmt::Debug,
|
||||
{
|
||||
/// The environment variable could not be read.
|
||||
///
|
||||
/// Encountered when the call to [`std::env::var`] fails.
|
||||
|
|
Loading…
Reference in a new issue