Add some documentation

This commit is contained in:
Lukas Kalbertodt
2021-05-16 15:59:33 +02:00
parent 366fd3d10a
commit b57382f546
2 changed files with 57 additions and 2 deletions

View File

@@ -16,11 +16,41 @@ pub mod internal;
pub mod source;
/// A configuration object that can be deserialized in layers via `serde`.
///
/// You would usually derive this trait for your own type and then load the
/// configuration with one of the provided methods, like
/// [`from_sources`][Self::from_sources].
///
/// # Deriving
///
/// This trait is usually derived as implementing it manually usually entails
/// writing some boilerplate code, that goes against the "don't repeat yourself"
/// principle.
///
/// TODO
pub trait Config: Sized {
/// A version of `Self` that represents a potetially partial configuration.
///
/// This type is supposed to have the exact same fields as this one, but
/// with every field being optional. Its main use is to have a layered
/// configuration from multiple sources where each layer might not contain
/// all required values. The only thing that matters is that combining all
/// layers will result in a configuration object that has all required
/// values defined.
type Partial: Partial;
/// Tries to create `Self` from a potentially partial object.
///
/// If any required values are not defined in `partial`, an [`Error`] is
/// returned.
fn from_partial(partial: Self::Partial) -> Result<Self, Error>;
/// Tries to load configuration values from all given sources, merging all
/// layers and returning the result. Sources earlier in the given slice have
/// a higher priority.
///
/// TODO: example
fn from_sources(sources: &[&dyn Source<Self>]) -> Result<Self, Error> {
let mut partial = Self::Partial::default_values();
for src in sources.iter().rev() {
@@ -32,18 +62,32 @@ pub trait Config: Sized {
}
}
/// A potentially partial configuration object that can be directly deserialized
/// via `serde`.
pub trait Partial: for<'de> Deserialize<'de> {
/// Returns `Self` where all fields/values are `None` or empty.
fn empty() -> Self;
/// Returns an object containing all default values (i.e. set via
/// `#[config(default = ...)]` when deriving `Config`) with all remaining
/// values/fields set to `None`/being empty.
fn default_values() -> Self;
/// 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
/// [`Option::or`].
fn with_fallback(self, fallback: Self) -> Self;
}
/// A source of configuration values for the configuration `T`, e.g. a file or
/// environment variables.
/// A source of configuration values for the configuration object `T`, e.g. a
/// file or environment variables.
pub trait Source<C: Config> {
/// Attempts to load a potentially partially configuration object.
fn load(&self) -> Result<C::Partial, Error>;
}
/// Type describing all errors that can occur in this library.
pub struct Error {
inner: Box<ErrorInner>,
}

View File

@@ -26,6 +26,12 @@ impl<C: Config> Source<C> for PathBuf {
}
}
/// A file as source for configuration.
///
/// Most of the time, you can problably use the [`Source`] impl for
/// `Path`/`PathBuf`, but this type gives you more control. For one, you can
/// explicitly set the file format. You can also mark a file as required,
/// meaning that an error will be returned if the file does not exist.
pub struct File {
path: PathBuf,
format: FileFormat,
@@ -102,12 +108,17 @@ impl<C: Config> Source<C> for File {
}
}
/// All file formats supported by confique.
///
/// All enum variants are `#[cfg]` guarded with the respective crate feature.
pub enum FileFormat {
#[cfg(feature = "toml")] Toml,
#[cfg(feature = "yaml")] Yaml,
}
impl FileFormat {
/// Guesses the file format from a file extension, returning `None` if the
/// extension is unknown or if the respective crate feature is not enabled.
pub fn from_extension(ext: impl AsRef<OsStr>) -> Option<Self> {
match ext.as_ref().to_str()? {
#[cfg(feature = "toml")]