mirror of
https://github.com/OMGeeky/confique.git
synced 2026-01-03 01:56:32 +01:00
Add Partial::from_env and implement it in derive macro
This commit is contained in:
@@ -120,6 +120,19 @@ fn gen_partial_mod(input: &ir::Input) -> TokenStream {
|
||||
}
|
||||
});
|
||||
|
||||
let from_env_fields = input.fields.iter().map(|f| {
|
||||
match &f.kind {
|
||||
FieldKind::Leaf { env: Some(key), .. } => {
|
||||
let field = format!("{}::{}", input.name, f.name);
|
||||
quote! {
|
||||
confique::internal::from_env(#key, #field)?
|
||||
}
|
||||
}
|
||||
FieldKind::Leaf { .. } => quote! { None },
|
||||
FieldKind::Nested { .. } => quote! { confique::Partial::from_env()? },
|
||||
}
|
||||
});
|
||||
|
||||
let fallbacks= input.fields.iter().map(|f| {
|
||||
let name = &f.name;
|
||||
if f.is_leaf() {
|
||||
@@ -174,6 +187,12 @@ fn gen_partial_mod(input: &ir::Input) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_env() -> Result<Self, confique::Error> {
|
||||
Ok(Self {
|
||||
#( #field_names: #from_env_fields, )*
|
||||
})
|
||||
}
|
||||
|
||||
fn with_fallback(self, fallback: Self) -> Self {
|
||||
Self {
|
||||
#( #field_names: #fallbacks, )*
|
||||
|
||||
17
src/error.rs
17
src/error.rs
@@ -28,6 +28,14 @@ pub(crate) enum ErrorInner {
|
||||
err: Box<dyn std::error::Error + Send + Sync>,
|
||||
},
|
||||
|
||||
/// When deserialization via `env` fails. The string is what is passed to
|
||||
/// `serde::de::Error::custom`.
|
||||
EnvDeserialization {
|
||||
field: String,
|
||||
key: String,
|
||||
msg: String,
|
||||
},
|
||||
|
||||
/// Returned by the [`Source`] impls for `Path` and `PathBuf` if the file
|
||||
/// extension is not supported by confique or if the corresponding Cargo
|
||||
/// feature of confique was not enabled.
|
||||
@@ -53,6 +61,7 @@ impl std::error::Error for Error {
|
||||
ErrorInner::Io { err, .. } => Some(err),
|
||||
ErrorInner::Deserialization { err, .. } => Some(&**err),
|
||||
ErrorInner::MissingValue(_)
|
||||
| ErrorInner::EnvDeserialization { .. }
|
||||
| ErrorInner::UnsupportedFileFormat { .. }
|
||||
| ErrorInner::MissingFileExtension { .. }
|
||||
| ErrorInner::MissingRequiredFile { .. } => None,
|
||||
@@ -81,6 +90,14 @@ impl fmt::Display for Error {
|
||||
ErrorInner::Deserialization { source: None, .. } => {
|
||||
std::write!(f, "failed to deserialize configuration")
|
||||
}
|
||||
ErrorInner::EnvDeserialization { field, key, msg } => {
|
||||
std::write!(f,
|
||||
"failed to deserialize value `{}` from environment variable `{}`: {}",
|
||||
field,
|
||||
key,
|
||||
msg,
|
||||
)
|
||||
}
|
||||
ErrorInner::UnsupportedFileFormat { path } => {
|
||||
std::write!(f,
|
||||
"unknown configuration file format/extension: '{}'",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! These functions are used by the code generated by the macro, but is not
|
||||
//! These functions are used by the code generated by the macro, but are not
|
||||
//! intended to be used directly. None of this is covered by semver! Do not use
|
||||
//! any of this directly.
|
||||
|
||||
@@ -24,3 +24,13 @@ pub fn prepend_missing_value_error(e: Error, prefix: &str) -> Error {
|
||||
e => e.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_env<'de, T: serde::Deserialize<'de>>(key: &str, field: &str) -> Result<T, Error> {
|
||||
crate::env::deserialize(std::env::var(key).ok()).map_err(|e| {
|
||||
ErrorInner::EnvDeserialization {
|
||||
key: key.into(),
|
||||
field: field.into(),
|
||||
msg: e.0,
|
||||
}.into()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -105,6 +105,15 @@ pub trait Partial: for<'de> Deserialize<'de> {
|
||||
/// values/fields set to `None`/being empty.
|
||||
fn default_values() -> Self;
|
||||
|
||||
/// Loads values from environment variables. This is only relevant for
|
||||
/// fields annotated with `#[config(env = "...")]`: all fields not
|
||||
/// annotated `env` will be `None`.
|
||||
///
|
||||
/// If the env variable corresponding to a field is not set, that field is
|
||||
/// `None`. If it is set but it failed to deserialize into the target type,
|
||||
/// an error is returned.
|
||||
fn from_env() -> Result<Self, Error>;
|
||||
|
||||
/// Combines two partial configuration objects. `self` has a higher
|
||||
/// priority; missing values in `self` are filled with values in `fallback`,
|
||||
/// if they exist. The semantics of this method is basically like in
|
||||
|
||||
Reference in New Issue
Block a user