use std::{error::Error, fmt};
use crate::{ConfigurationBuilder, Source};
pub struct OffsetSource<'a, OffsetBuilder, PathFn> {
inner_source: Box<dyn Source<OffsetBuilder> + 'a>,
path: PathFn,
}
impl<'a, OffsetBuilder, PathFn> OffsetSource<'a, OffsetBuilder, PathFn>
where
OffsetBuilder: ConfigurationBuilder,
{
pub fn new<TargetBuilder>(inner_source: impl Source<OffsetBuilder> + 'a, path: PathFn) -> Self
where
TargetBuilder: ConfigurationBuilder,
PathFn: for<'b> Fn(&'b mut TargetBuilder) -> &'b mut OffsetBuilder,
{
Self {
inner_source: Box::new(inner_source),
path,
}
}
}
impl<'a, OffsetBuilder, PathFn, TargetBuilder> Source<TargetBuilder>
for OffsetSource<'a, OffsetBuilder, PathFn>
where
TargetBuilder: ConfigurationBuilder,
OffsetBuilder: ConfigurationBuilder,
PathFn: for<'b> Fn(&'b mut TargetBuilder) -> &'b mut OffsetBuilder,
{
fn allows_secrets(&self) -> bool {
self.inner_source.allows_secrets()
}
fn provide(&self) -> Result<TargetBuilder, Box<dyn Error + Sync + Send>> {
let mut builder = TargetBuilder::default();
*(self.path)(&mut builder) = self.inner_source.provide()?;
Ok(builder)
}
}
impl<OffsetBuilder, PathFn> fmt::Debug for OffsetSource<'_, OffsetBuilder, PathFn> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OffsetSource")
.field("inner_source", &self.inner_source)
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use crate::{helpers::BuilderOf, sources::test::TestSource, Configuration, OffsetSource};
#[derive(Debug, Configuration, PartialEq, Eq)]
#[confik(forward(derive(Clone)))]
struct Config {
#[confik(default)]
data: usize,
leaf: LeafConfig,
}
#[derive(Debug, Configuration, PartialEq, Eq)]
#[confik(forward(derive(Clone)))]
struct LeafConfig {
#[confik(default)]
data: usize,
}
#[test]
fn identity_offset() {
let test_source_builder = BuilderOf::<Config> {
data: Some(6),
..Default::default()
};
let inner = TestSource {
data: test_source_builder,
allow_secrets: false,
};
let source = OffsetSource::new(inner, |x| x);
let config = Config::builder()
.override_with(source)
.try_build()
.expect("Valid input");
assert_eq!(
config,
Config {
data: 6,
leaf: LeafConfig { data: 0 }
}
)
}
#[test]
fn leaf_offset() {
let test_source_builder = BuilderOf::<LeafConfig> { data: Some(6) };
let inner = TestSource {
data: test_source_builder,
allow_secrets: false,
};
let source = OffsetSource::new::<BuilderOf<Config>>(inner, |x| &mut x.leaf);
let config = Config::builder()
.override_with(source)
.try_build()
.expect("Valid input");
assert_eq!(
config,
Config {
data: 0,
leaf: LeafConfig { data: 6 }
}
)
}
#[test]
#[cfg(feature = "json")]
fn data_offset_json() {
let data_source =
OffsetSource::new(crate::JsonSource::new("1"), |x: &mut BuilderOf<Config>| {
&mut x.data
});
let leaf_source =
OffsetSource::new(crate::JsonSource::new("2"), |x: &mut BuilderOf<Config>| {
&mut x.leaf.data
});
let config = Config::builder()
.override_with(data_source)
.override_with(leaf_source)
.try_build()
.expect("Valid input");
assert_eq!(
config,
Config {
data: 1,
leaf: LeafConfig { data: 2 }
}
)
}
#[test]
#[cfg(feature = "env")]
fn data_offset_env() {
temp_env::with_var("data", Some("10"), || {
let data_source = OffsetSource::new(crate::EnvSource::new(), |x| x);
let leaf_source =
OffsetSource::new(crate::EnvSource::new(), |x: &mut BuilderOf<Config>| {
&mut x.leaf
});
let config = Config::builder()
.override_with(data_source)
.override_with(leaf_source)
.try_build()
.expect("Valid input");
assert_eq!(
config,
Config {
data: 10,
leaf: LeafConfig { data: 10 }
}
)
})
}
}