1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
//! A general builder struct to allow for adding sources gradually, instead of requiring
//! producing your own list for [`build_from_sources`].
//!
//! The sources are consumed in the order they are provided, with priority given to the first
//! source. E.g., If sources `Source::File("config.toml")` and `Source::File("defaults.toml")` are
//! provided any values specified in `config.toml` take precedence over `defaults.toml`.
//!
//! A builder will generally be created by calling [`ConfigBuilder::default`], sources will be added
//! with [`ConfigBuilder::override_with`] which overrides existing source with the new source, and
//! then your configuration built with [`ConfigBuilder::try_build`].
use std::{marker::PhantomData, mem};
use confik::sources::DefaultSource;
use crate::{
build_from_sources,
sources::{DynSource, Source},
Configuration, Error,
};
/// Used to accumulate ordered sources from which its `Target` is to be built.
///
/// An instance of this can be created via [`Configuration::builder`] or
/// [`ConfigBuilder::<T>::default`].
///
/// # Examples
///
/// Using [`Configuration::builder`]:
///
/// ```
/// # #[cfg(feature = "toml")]
/// # {
/// use confik::{Configuration, TomlSource};
///
/// #[derive(Debug, PartialEq, Configuration)]
/// struct MyConfigType {
/// param: String,
/// }
///
/// let config = MyConfigType::builder()
/// .override_with(TomlSource::new(r#"param = "Hello World""#))
/// .try_build()
/// .expect("Failed to build");
///
/// assert_eq!(config.param, "Hello World");
/// # }
/// ```
///
/// Using [`ConfigBuilder::<T>::default`]:
///
/// ```
/// # #[cfg(feature = "toml")]
/// # {
/// use confik::{ConfigBuilder, Configuration, TomlSource};
///
/// #[derive(Debug, PartialEq, Configuration)]
/// struct MyConfigType {
/// param: String,
/// }
///
/// let config = ConfigBuilder::<MyConfigType>::default()
/// .override_with(TomlSource::new(r#"param = "Hello World""#))
/// .try_build()
/// .expect("Failed to build");
///
/// assert_eq!(config.param, "Hello World");
/// # }
/// ```
pub struct ConfigBuilder<'a, Target: Configuration> {
sources: Vec<Box<dyn DynSource<Target::Builder> + 'a>>,
/// Use the generic parameter
_phantom: PhantomData<fn() -> Target>,
}
impl<'a, Target: Configuration> ConfigBuilder<'a, Target> {
/// Add a single [`Source`] to the list of sources.
///
/// The source is added at the end of the list, overriding existing sources.
///
/// ```
/// # #[cfg(feature = "toml")]
/// # {
/// use confik::{Configuration, TomlSource};
/// #[derive(Debug, PartialEq, Configuration)]
/// struct MyConfigType {
/// param: String,
/// }
///
/// let config = MyConfigType::builder()
/// .override_with(TomlSource::new(r#"param = "Hello World""#))
/// .override_with(TomlSource::new(r#"param = "Hello Universe""#))
/// .try_build()
/// .expect("Failed to build");
///
/// assert_eq!(config.param, "Hello Universe");
/// # }
/// ```
pub fn override_with(&mut self, source: impl Source + 'a) -> &mut Self {
self.sources.push(Box::new(source));
self
}
/// Attempt to build from the provided sources.
pub fn try_build(&mut self) -> Result<Target, Error> {
if self.sources.is_empty() {
build_from_sources([Box::new(DefaultSource) as Box<dyn DynSource<_>>])
} else {
build_from_sources(mem::take(&mut self.sources).into_iter().rev())
}
}
}
impl<'a, Target: Configuration> Default for ConfigBuilder<'a, Target> {
fn default() -> Self {
Self {
sources: Vec::new(),
_phantom: PhantomData,
}
}
}