Skip to content
Snippets Groups Projects

Figment Backend

Merged Eduardo Trujillo requested to merge figment into master
Files
5
+ 102
34
use std::{
env, fs,
env,
path::{Path, PathBuf},
};
use log::info;
use serde::de::DeserializeOwned;
use figment::{
providers::{Env, Format, Serialized, Toml},
Figment, Profile,
};
use log::{debug, info};
use serde::{de::DeserializeOwned, Serialize};
use thiserror::Error;
use crate::str::to_train_case;
#[derive(Error, Debug)]
pub enum ConfigError {
/// The configuration file could not be found or read.
@@ -32,6 +38,11 @@ pub enum ConfigError {
/// None of the provided paths were valid configuration files.
#[error("None of the provided paths were valid configuration files.")]
NoValidPath,
#[error("Unable to extract config from providers: {source:?}")]
ExtractConfig {
#[from]
source: figment::Error,
},
}
/// Attempts to read a Config object from the specified path.
@@ -42,38 +53,25 @@ pub fn from_file<P: AsRef<Path>, C: DeserializeOwned>(path: P) -> Result<C, Conf
info!("Reading config file from {}", path.display());
let contents = fs::read_to_string(path).map_err(|source| ConfigError::OpenConfig {
path: path.into(),
source,
})?;
let config = toml::from_str(&contents);
config.map_err(|source| ConfigError::DeserializeConfig {
path: path.into(),
source,
})
Figment::new()
.merge(Toml::file(path))
.extract()
.map_err(|source| ConfigError::ExtractConfig { source })
}
/// Attempts to read a Config object from the specified paths.
pub fn from_paths<P: AsRef<Path>, C: DeserializeOwned>(paths: Vec<P>) -> Result<C, ConfigError> {
for path in paths {
if path.as_ref().exists() {
return from_file(path);
}
match get_first_valid_path(paths) {
Some(path) => from_file(path),
None => Err(ConfigError::NoValidPath),
}
Err(ConfigError::NoValidPath)
}
/// Attempts to read a Config object from the current directory.
///
/// The configuration file is expected to be a TOML file named `config.toml`.
pub fn from_current_dir<C: DeserializeOwned>() -> Result<C, ConfigError> {
let mut current_path =
env::current_dir().map_err(|source| ConfigError::GetCurrentDir { source })?;
current_path.push("config.toml");
let current_path = get_current_dir_config_path()?;
from_file(&current_path)
}
@@ -86,19 +84,74 @@ pub fn from_default_paths<P: AsRef<Path>, C: DeserializeOwned>(
application_name: &str,
additional_paths: &[P],
) -> Result<C, ConfigError> {
let mut current_path =
env::current_dir().map_err(|source| ConfigError::GetCurrentDir { source })?;
let mut paths = as_paths(additional_paths);
current_path.push("config.toml");
paths.push(get_current_dir_config_path()?);
paths.push(get_name_config_path(application_name));
let mut config_path = PathBuf::from("/etc/");
let figment = match get_first_valid_path(paths) {
Some(path) => {
info!("Reading config file from {}", path.display());
config_path.push(application_name);
config_path.push("config.toml");
Ok(Figment::from(Toml::file(path)))
}
None => Err(ConfigError::NoValidPath),
}?;
figment
.extract()
.map_err(|source| ConfigError::ExtractConfig { source })
}
/// Similar to `from_paths`. Uses a default set of paths:
///
/// - CURRENT_WORKING_DIRECTORY/config.toml
/// - /etc/CRATE/config.toml
pub fn from_defaults<P: AsRef<Path>, C: DeserializeOwned + Default + Serialize>(
application_name: &str,
additional_paths: &[P],
) -> Result<C, ConfigError> {
let mut paths = as_paths(additional_paths);
paths.push(get_current_dir_config_path()?);
paths.push(get_name_config_path(application_name));
let default_config: C = Default::default();
let figment = Figment::from(Serialized::from(default_config, Profile::default()));
let figment = match get_first_valid_path(paths) {
Some(path) => {
info!("Reading config file from {}", path.display());
figment.merge(Toml::file(path))
}
None => figment,
};
let env_prefix = format!("{}_", to_train_case(application_name));
debug!("Using env prefix: {}", &env_prefix);
figment
.merge(Env::prefixed(&env_prefix))
.extract()
.map_err(|source| ConfigError::ExtractConfig { source })
}
pub fn get_first_valid_path<P: AsRef<Path>>(paths: Vec<P>) -> Option<P> {
for path in paths {
if path.as_ref().exists() {
return Some(path);
}
}
None
}
fn as_paths<P: AsRef<Path>>(path_refs: &[P]) -> Vec<PathBuf> {
let mut paths: Vec<PathBuf> = vec![];
for path in additional_paths {
for path in path_refs {
let mut pathbuf = PathBuf::new();
pathbuf.push(path);
@@ -106,8 +159,23 @@ pub fn from_default_paths<P: AsRef<Path>, C: DeserializeOwned>(
paths.push(pathbuf);
}
paths.push(current_path);
paths.push(config_path);
paths
}
fn get_current_dir_config_path() -> Result<PathBuf, ConfigError> {
let mut current_path =
env::current_dir().map_err(|source| ConfigError::GetCurrentDir { source })?;
current_path.push("config.toml");
Ok(current_path)
}
fn get_name_config_path(application_name: &str) -> PathBuf {
let mut config_path = PathBuf::from("/etc/");
config_path.push(application_name);
config_path.push("config.toml");
from_paths(paths)
config_path
}
Loading