mirror of
https://github.com/Steffo99/micronfig.git
synced 2024-11-21 23:54:20 +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::var;
|
||||||
use crate::file;
|
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:
|
/// This function tries to get a configuration value:
|
||||||
///
|
///
|
||||||
/// 1. with [`var::get`] using `name_var`, returning a [`Source::Var`]
|
/// 1. with [`var::get`] using `name`, returning a [`Source::Var`]
|
||||||
/// 2. with [`file::get`] using `name_file`, returning a [`Source::File`]
|
/// 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`]
|
/// - [`var::Error::CannotReadEnvVar`]
|
||||||
/// - [`file::Error::CannotReadEnvVar`]
|
/// - [`file::Error::CannotReadEnvVar`]
|
||||||
///
|
///
|
||||||
|
@ -27,7 +34,7 @@ use crate::file;
|
||||||
/// # std::env::set_var("NUMBER", "1");
|
/// # std::env::set_var("NUMBER", "1");
|
||||||
/// # std::env::remove_var("NUMBER_FILE");
|
/// # 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!() }
|
/// 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");
|
||||||
/// # std::env::remove_var("NUMBER_FILE");
|
/// # 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!() }
|
/// 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>,
|
where KeyVar: AsRef<std::ffi::OsStr>,
|
||||||
KeyFile: AsRef<std::ffi::OsStr>,
|
KeyFile: AsRef<std::ffi::OsStr>,
|
||||||
Type: std::str::FromStr,
|
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 {
|
match v {
|
||||||
Err(var::Error::CannotReadEnvVar(_)) => {},
|
Err(var::Error::CannotReadEnvVar(_)) => {},
|
||||||
_ => return Source::Var(v),
|
_ => 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);
|
let v = file::get(name_file);
|
||||||
|
|
||||||
match v {
|
match v {
|
||||||
|
@ -72,6 +83,7 @@ pub fn get<KeyVar, KeyFile, Type>(name_var: KeyVar, name_file: KeyFile) -> Sourc
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Source<Type>
|
pub enum Source<Type>
|
||||||
where Type: std::str::FromStr,
|
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.
|
/// The result was not obtained, since the configuration value was not defined anywhere.
|
||||||
NotFound,
|
NotFound,
|
||||||
|
@ -85,6 +97,7 @@ pub enum Source<Type>
|
||||||
|
|
||||||
impl<Type> Source<Type>
|
impl<Type> Source<Type>
|
||||||
where Type: std::str::FromStr,
|
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.
|
/// 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`].
|
/// 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
|
/// # See also
|
||||||
///
|
///
|
||||||
/// Similar to [`Result::unwrap`].
|
/// Similar to [`Result::unwrap`].
|
||||||
|
@ -171,7 +182,7 @@ pub(crate) mod tests {
|
||||||
std::env::set_var("NUMBER", "1");
|
std::env::set_var("NUMBER", "1");
|
||||||
std::env::remove_var("NUMBER_FILE");
|
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)) => {},
|
Source::Var(Ok(1u32)) => {},
|
||||||
_ => panic!("expected 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::remove_var("NUMBER");
|
||||||
std::env::set_var("NUMBER_FILE", file.as_os_str());
|
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 {
|
match n {
|
||||||
Source::File(Ok(1u32)) => {},
|
Source::File(Ok(1u32)) => {},
|
||||||
_ => panic!("expected Source::File(Ok(1u32))")
|
_ => panic!("expected Source::File(Ok(1u32))")
|
||||||
|
@ -192,7 +203,7 @@ pub(crate) mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_envvar() {
|
fn missing_envvar() {
|
||||||
match get::<&str, &str, String>("MISSING_ENVVAR", "MISSING_ENVVAR_FILE") {
|
match get::<&str, &str, String>("MISSING_ENVVAR", "_FILE") {
|
||||||
Source::NotFound => {},
|
Source::NotFound => {},
|
||||||
_ => panic!("expected Source::NotFound"),
|
_ => panic!("expected Source::NotFound"),
|
||||||
}
|
}
|
||||||
|
@ -203,7 +214,7 @@ pub(crate) mod tests {
|
||||||
std::env::remove_var("NUMBER");
|
std::env::remove_var("NUMBER");
|
||||||
std::env::set_var("NUMBER_FILE", "/this/file/does/not/exist");
|
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(_))) => {},
|
Source::File(Err(file::Error::CannotOpenFile(_))) => {},
|
||||||
_ => panic!("expected 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::set_var("NUMBER", "XYZ");
|
||||||
std::env::remove_var("NUMBER_FILE");
|
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(_))) => {},
|
Source::Var(Err(var::Error::CannotConvertValue(_))) => {},
|
||||||
_ => panic!("expected 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::set_var("NUMBER_FILE", file.as_os_str());
|
||||||
std::env::remove_var("NUMBER");
|
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(_))) => {},
|
Source::File(Err(file::Error::CannotConvertValue(_))) => {},
|
||||||
_ => panic!("expected 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>
|
pub fn get<Key, Type>(name: Key) -> Result<Type>
|
||||||
where Key: AsRef<std::ffi::OsStr>,
|
where Key: AsRef<std::ffi::OsStr>,
|
||||||
Type: std::str::FromStr,
|
Type: std::str::FromStr,
|
||||||
|
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||||
{
|
{
|
||||||
let path = std::env::var(name)
|
let path = std::env::var(name)
|
||||||
.map_err(Error::CannotReadEnvVar)?;
|
.map_err(Error::CannotReadEnvVar)?;
|
||||||
|
@ -27,8 +28,9 @@ pub fn get<Key, Type>(name: Key) -> Result<Type>
|
||||||
|
|
||||||
|
|
||||||
/// A possible error encountered by [`get`].
|
/// A possible error encountered by [`get`].
|
||||||
#[derive(Debug)]
|
#[derive(std::fmt::Debug)]
|
||||||
pub enum Error<ConversionError>
|
pub enum Error<ConversionError>
|
||||||
|
where ConversionError: std::fmt::Debug,
|
||||||
{
|
{
|
||||||
/// The environment variable could not be read.
|
/// The environment variable could not be read.
|
||||||
///
|
///
|
||||||
|
|
143
src/lib.rs
143
src/lib.rs
|
@ -8,30 +8,135 @@
|
||||||
//!
|
//!
|
||||||
//! This crate handles:
|
//! This crate handles:
|
||||||
//!
|
//!
|
||||||
//! 1. Retrieval of configuration values from multiple sources ([`any::get`])
|
//! 1. Retrieval of configuration values from multiple sources
|
||||||
//! 1. The environment ([`var::get`])
|
//! 1. The environment
|
||||||
//! 2. Files specified in the environment ([`file::get`])
|
//! 2. Files specified in the environment
|
||||||
//! 2. Conversion to a value of an arbitrary type ([`std::str::FromStr`])
|
//! 2. Conversion to a value of an arbitrary type
|
||||||
//! 3. Displaying a operator-friendly error in case
|
//! 3. Displaying a operator-friendly error if case one of this steps did not succeed
|
||||||
//!
|
|
||||||
//! # 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!()
|
|
||||||
//! ```
|
|
||||||
|
|
||||||
pub mod any;
|
pub mod any;
|
||||||
pub mod var;
|
pub mod var;
|
||||||
pub mod file;
|
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)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
pub fn get<Key, Type>(name: Key) -> Result<Type>
|
pub fn get<Key, Type>(name: Key) -> Result<Type>
|
||||||
where Key: AsRef<std::ffi::OsStr>,
|
where Key: AsRef<std::ffi::OsStr>,
|
||||||
Type: std::str::FromStr,
|
Type: std::str::FromStr,
|
||||||
|
<Type as std::str::FromStr>::Err: std::fmt::Debug,
|
||||||
{
|
{
|
||||||
let data = std::env::var(name)
|
let data = std::env::var(name)
|
||||||
.map_err(Error::CannotReadEnvVar)?;
|
.map_err(Error::CannotReadEnvVar)?;
|
||||||
|
@ -17,8 +18,10 @@ pub fn get<Key, Type>(name: Key) -> Result<Type>
|
||||||
|
|
||||||
|
|
||||||
/// A possible error encountered by [`get`].
|
/// A possible error encountered by [`get`].
|
||||||
#[derive(Debug)]
|
#[derive(std::fmt::Debug)]
|
||||||
pub enum Error<ConversionError> {
|
pub enum Error<ConversionError>
|
||||||
|
where ConversionError: std::fmt::Debug,
|
||||||
|
{
|
||||||
/// The environment variable could not be read.
|
/// The environment variable could not be read.
|
||||||
///
|
///
|
||||||
/// Encountered when the call to [`std::env::var`] fails.
|
/// Encountered when the call to [`std::env::var`] fails.
|
||||||
|
|
Loading…
Reference in a new issue