Files
confique/src/internal.rs
Lukas Kalbertodt e2dded17fa Change parse_env error type from Debug to impl std::error::Error
This is the more appropriate trait I think and should work well for
most real world use cases.
2022-11-06 11:23:57 +01:00

130 lines
3.9 KiB
Rust

//! 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.
use crate::{error::ErrorInner, Error};
pub fn deserialize_default<I, O>(src: I) -> Result<O, serde::de::value::Error>
where
I: for<'de> serde::de::IntoDeserializer<'de>,
O: for<'de> serde::Deserialize<'de>,
{
O::deserialize(src.into_deserializer())
}
pub fn into_deserializer<'de, T>(src: T) -> <T as serde::de::IntoDeserializer<'de>>::Deserializer
where
T: serde::de::IntoDeserializer<'de>,
{
src.into_deserializer()
}
pub fn unwrap_or_missing_value_err<T>(value: Option<T>, path: &str) -> Result<T, Error> {
match value {
Some(v) => Ok(v),
None => Err(ErrorInner::MissingValue(path.into()).into()),
}
}
pub fn map_err_prefix_path<T>(res: Result<T, Error>, prefix: &str) -> Result<T, Error> {
res.map_err(|e| {
if let ErrorInner::MissingValue(path) = &*e.inner {
ErrorInner::MissingValue(format!("{prefix}.{path}")).into()
} else {
e
}
})
}
macro_rules! get_env_var {
($key:expr, $field:expr) => {
match std::env::var($key) {
Err(std::env::VarError::NotPresent) => return Ok(None),
Err(std::env::VarError::NotUnicode(_)) => {
let err = ErrorInner::EnvNotUnicode {
key: $key.into(),
field: $field.into(),
};
return Err(err.into());
}
Ok(s) => s,
}
};
}
pub fn from_env<'de, T: serde::Deserialize<'de>>(
key: &str,
field: &str,
) -> Result<Option<T>, Error> {
from_env_with_deserializer(key, field, |de| T::deserialize(de))
}
pub fn from_env_with_parser<T, E: std::error::Error + Send + Sync + 'static>(
key: &str,
field: &str,
parse: fn(&str) -> Result<T, E>,
) -> Result<Option<T>, Error> {
let v = get_env_var!(key, field);
parse(&v)
.map(Some)
.map_err(|err| {
ErrorInner::EnvParseError {
field: field.to_owned(),
key: key.to_owned(),
err: Box::new(err),
}.into()
})
}
pub fn from_env_with_deserializer<T>(
key: &str,
field: &str,
deserialize: fn(crate::env::Deserializer) -> Result<T, crate::env::DeError>,
) -> Result<Option<T>, Error> {
let s = get_env_var!(key, field);
match deserialize(crate::env::Deserializer::new(s)) {
Ok(v) => Ok(Some(v)),
Err(e) => Err(ErrorInner::EnvDeserialization {
key: key.into(),
field: field.into(),
msg: e.0,
}.into()),
}
}
/// `serde` does not implement `IntoDeserializer` for fixed size arrays. This
/// helper type is just used for this purpose.
pub struct ArrayIntoDeserializer<T, const N: usize>(pub [T; N]);
impl<'de, T, E, const N: usize> serde::de::IntoDeserializer<'de, E> for ArrayIntoDeserializer<T, N>
where
T: serde::de::IntoDeserializer<'de, E>,
E: serde::de::Error,
{
type Deserializer = serde::de::value::SeqDeserializer<std::array::IntoIter<T, N>, E>;
fn into_deserializer(self) -> Self::Deserializer {
serde::de::value::SeqDeserializer::new(self.0.into_iter())
}
}
/// `serde` does implement `IntoDeserializer` for `HashMap` and `BTreeMap` but
/// we want to keep the exact source code order of entries, so we need our own
/// type.
pub struct MapIntoDeserializer<K, V>(pub Vec<(K, V)>);
impl<'de, K, V, E> serde::de::IntoDeserializer<'de, E> for MapIntoDeserializer<K, V>
where
K: serde::de::IntoDeserializer<'de, E>,
V: serde::de::IntoDeserializer<'de, E>,
E: serde::de::Error,
{
type Deserializer = serde::de::value::MapDeserializer<'de, std::vec::IntoIter<(K, V)>, E>;
fn into_deserializer(self) -> Self::Deserializer {
serde::de::value::MapDeserializer::new(self.0.into_iter())
}
}