1
Fork 0
mirror of https://github.com/Steffo99/micronfig.git synced 2024-10-16 06:27:28 +00:00

Improve and fix documentation

This commit is contained in:
Steffo 2023-04-29 01:57:14 +02:00
parent d414d3cc90
commit a457be6a31
Signed by: steffo
GPG key ID: 2A24051445686895
4 changed files with 114 additions and 34 deletions

View file

@ -5,14 +5,14 @@ use crate::var;
use crate::file;
/// 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.
/// Get a 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`, returning a [`Source::Var`]
/// 2. with [`file::get`] using `name + file_suffix`, returning a [`Source::File`]
/// 1. with [`var::get`] using `key`, returning a [`Source::Var`]
/// 2. with [`file::get`] using `key + key_suffix_file`, returning a [`Source::File`]
///
/// If none of these options successfully resulted in the successful retrieval of the configuration value, [`Source::NotFound`] is returned instead.
///
@ -50,23 +50,23 @@ use crate::file;
/// if let Source::NotFound = value {} else { panic!() }
/// ```
///
pub fn get<KeyVar, KeyFile, Type>(name: KeyVar, file_suffix: KeyFile) -> Source<Type>
where KeyVar: AsRef<std::ffi::OsStr>,
KeyFile: AsRef<std::ffi::OsStr>,
pub fn get<Key, KeySuffixFile, Type>(key: Key, key_suffix_file: KeySuffixFile) -> Source<Type>
where Key: AsRef<std::ffi::OsStr>,
KeySuffixFile: AsRef<std::ffi::OsStr>,
Type: std::str::FromStr,
<Type as std::str::FromStr>::Err: std::fmt::Debug,
{
let v = var::get(&name);
let v = var::get(&key);
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);
let mut key_file = OsString::new();
key_file.push(key);
key_file.push(key_suffix_file);
let v = file::get(key_file);
match v {
Err(file::Error::CannotReadEnvVar(_)) => {},

View file

@ -1,13 +1,13 @@
//! Module defining the [`get`] low-level function for environment files, and its associated types.
/// Get a configuration value from the file at the path contained in the environment variable with the given `name`, and convert it to the desired `Type`.
pub fn get<Key, Type>(name: Key) -> Result<Type>
/// Get a configuration value from the file at the path contained in the environment variable with the given `key`, and convert it to the desired `Type`.
pub fn get<Key, Type>(key: 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)
let path = std::env::var(key)
.map_err(Error::CannotReadEnvVar)?;
let path = std::ffi::OsString::from(path);
let path = std::path::PathBuf::from(path);

View file

@ -1,24 +1,104 @@
//! Tiny crate for [twelve-factor app configuration](https://12factor.net/config).
//!
//! # Goals
//!
//! This crate aims to simplify developing and deploying Docker-compatible services in Rust.
//!
//! # Features
//!
//! This crate handles:
//!
//! 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
//! - 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
//!
//! Each configurable property of the dependent binary must have an arbitrary *key*, a name used to define its value, usually in `SCREAMING_SNAKE_CASE`.
//!
//! For example, some keys may be:
//!
//! - `TELEGRAM_API_KEY`
//! - `OAUTH2_CLIENT_SECRET`
//! - `SCREEN_RESOLUTION`
//!
//! ## High-level API
//!
//! The recommended usage of this crate is via the high-level API, which comprises the [`required`] and [`optional`] functions.
//!
//! They automatically try to retrieve a value from the following sources, in this order, returning as soon as one is found:
//!
//! 1. the contents of the environment variable `{key}`;
//! 2. the contents of the file located at path specified in the environment variable `{key}_FILE`.
//!
//! If no value is found, or if an error occurred while trying to retrieve it, the function panics with a human-readable error message.
//!
//! Additionally, they try to parse the value into the requested Rust type using its [`FromStr`] trait.
//!
//! If the conversion fails, the function panics, again providing a human-readable error message.
//!
//! ### Examples
//!
//! To require a `IP_ADDRESS` property to be configured, and to parse it as an [`IpAddr`], you may write the following code:
//!
//! ```
//! use std::net::IpAddr;
//!
//! # std::env::set_var("IP_ADDRESS", "192.168.1.1");
//! let ip_addr: IpAddr = micronfig::required("IP_ADDRESS");
//! ```
//!
//! To allow the user to not specify it, and provide a default, you may write:
//!
//! ```
//! use std::net::{IpAddr, Ipv4Addr};
//!
//! # std::env::remove_var("IP_ADDRESS");
//! let ip_addr: IpAddr = micronfig::optional("IP_ADDRESS").unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST));
//! ```
//!
//! ## Middle-level API
//!
//! If you want more control on how errors are handled or on how the key is manipulated to access values, you can use the middle-level API, comprised of the [`any::get`] function and the [`any::Source`] enum.
//!
//! [`any::get`] works similarly to the [`required`] and [`optional`] functions, but returns a [`any::Source`] enum variant instead, which denotes the source a result was obtained from, and contains the raw [`Result`] of the operation.
//!
//! ### Example
//!
//! To customize the handling of the same `IP_ADDRESS` as earlier, so that something is printed instead of the binary panicking, you may write the following code:
//!
//! ```
//! use std::net::IpAddr;
//! use micronfig::any::{get, Source};
//!
//! let ip_addr: Source<IpAddr> = get("IP_ADDRESS", "_FILE");
//!
//! match ip_addr {
//! Source::Var(Ok(addr)) | Source::File(Ok(addr)) => println!("Success! · {}", &addr),
//! _ => println!("Failure..."),
//! }
//! ```
//!
//! ## Low-level API
//!
//! Finally, if you want to override the accessed sources, you may use the low level API directly, comprised of the following modules:
//!
//! - [`var`] for accessing environment variable
//! - [`file`] for accessing files with the path defined in environment variables
//!
//! ### Example
//!
//! To retrieve the `IP_ADDRESS` only from the environment variable, and ignoring other sources:
//!
//! ```
//! use std::net::IpAddr;
//! use micronfig::var::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 mod any;
pub mod var;
pub mod file;
/// Get the configuration value with the given `name` and convert it to the given `Type`.
/// Get the configuration value with the given `key` and convert it to the given `Type`.
///
/// # Panics
///
@ -50,31 +130,31 @@ pub mod file;
///
/// [`any::get`], the function called by this one to get the configuration value.
///
pub fn required<Type>(name: &str) -> Type
pub fn required<Type>(key: &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") {
match get(key, "_FILE") {
Source::Var(Ok(v)) => v,
Source::Var(Err(var::Error::CannotConvertValue(err))) =>
panic!("The contents of the {} environment variable could not be converted to a {}: {:?}", &name, &std::any::type_name::<Type>(), &err),
panic!("The contents of the {} environment variable could not be converted to a {}: {:?}", &key, &std::any::type_name::<Type>(), &err),
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(err))) =>
panic!("The contents of the file at {} could not be converted to a {}: {:?}", &name, &std::any::type_name::<Type>(), &err),
panic!("The contents of the file at {} could not be converted to a {}: {:?}", &key, &std::any::type_name::<Type>(), &err),
Source::File(Err(file::Error::CannotOpenFile(err))) =>
panic!("The file at {} could not be opened: {}", &name, &err),
panic!("The file at {} could not be opened: {}", &key, &err),
Source::File(Err(file::Error::CannotReadFile(err))) =>
panic!("The contents of the file at {} could not be read: {}", &name, &err),
panic!("The contents of the file at {} could not be read: {}", &key, &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),
panic!("The configuration value {} is not defined.", &key),
}
}

View file

@ -1,13 +1,13 @@
//! Module defining the [`get`] low-level function for environment variables, and its associated types.
/// Get a configuration value from the environment variable with the given `name`, and convert it to the desired `Type`.
pub fn get<Key, Type>(name: Key) -> Result<Type>
/// Get a configuration value from the environment variable with the given `key`, and convert it to the desired `Type`.
pub fn get<Key, Type>(key: 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)
let data = std::env::var(key)
.map_err(Error::CannotReadEnvVar)?;
let value = Type::from_str(&data)