diff --git a/Cargo.toml b/Cargo.toml index f281085..6a00d42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,11 @@ categories = ["config"] exclude = [".github"] +[[example]] +name = "simple" +required-features = ["toml"] + + [features] default = ["toml", "yaml"] yaml = ["serde_yaml"] diff --git a/src/builder.rs b/src/builder.rs index 31c224b..939abd1 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,6 +1,11 @@ +#[cfg(any(feature = "toml", feature = "yaml"))] use std::path::PathBuf; -use crate::{Config, Error, File, Partial}; +use crate::{Config, Error, Partial}; + +#[cfg(any(feature = "toml", feature = "yaml"))] +use crate::File; + /// Convenience builder to configure, load and merge multiple configuration @@ -25,6 +30,7 @@ impl Builder { /// /// The file is not considered required: if the file does not exist, an /// empty configuration (`C::Partial::empty()`) is used for this layer. + #[cfg(any(feature = "toml", feature = "yaml"))] pub fn file(mut self, path: impl Into) -> Self { self.sources.push(Source::File(path.into())); self @@ -51,6 +57,7 @@ impl Builder { let mut partial = C::Partial::empty(); for source in self.sources { let layer = match source { + #[cfg(any(feature = "toml", feature = "yaml"))] Source::File(path) => File::new(path)?.load()?, Source::Env => C::Partial::from_env()?, Source::Preloaded(p) => p, @@ -64,6 +71,7 @@ impl Builder { } enum Source { + #[cfg(any(feature = "toml", feature = "yaml"))] File(PathBuf), Env, Preloaded(C::Partial), diff --git a/src/error.rs b/src/error.rs index 017a417..136bcb5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,8 @@ -use std::{fmt, path::PathBuf}; +use std::fmt; + +#[cfg(any(feature = "toml", feature = "yaml"))] +use std::path::PathBuf; + /// Type describing all errors that can occur in this library. @@ -13,12 +17,14 @@ pub(crate) enum ErrorInner { MissingValue(String), /// An IO error occured, e.g. when reading a file. + #[cfg(any(feature = "toml", feature = "yaml"))] Io { path: Option, err: std::io::Error, }, /// Returned by `Source::load` implementations when deserialization fails. + #[cfg(any(feature = "toml", feature = "yaml"))] Deserialization { /// A human readable description for the error message, describing from /// what source it was attempted to deserialize. Completes the sentence @@ -39,17 +45,20 @@ pub(crate) enum ErrorInner { /// 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. + #[cfg(any(feature = "toml", feature = "yaml"))] UnsupportedFileFormat { path: PathBuf, }, /// Returned by the [`Source`] impls for `Path` and `PathBuf` if the path /// does not contain a file extension. + #[cfg(any(feature = "toml", feature = "yaml"))] MissingFileExtension { path: PathBuf, }, /// A file source was marked as required but the file does not exist. + #[cfg(any(feature = "toml", feature = "yaml"))] MissingRequiredFile { path: PathBuf, } @@ -58,13 +67,18 @@ pub(crate) enum ErrorInner { impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match &*self.inner { + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::Io { err, .. } => Some(err), + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::Deserialization { err, .. } => Some(&**err), - ErrorInner::MissingValue(_) - | ErrorInner::EnvDeserialization { .. } - | ErrorInner::UnsupportedFileFormat { .. } - | ErrorInner::MissingFileExtension { .. } - | ErrorInner::MissingRequiredFile { .. } => None, + ErrorInner::MissingValue(_) => None, + ErrorInner::EnvDeserialization { .. } => None, + #[cfg(any(feature = "toml", feature = "yaml"))] + ErrorInner::UnsupportedFileFormat { .. } => None, + #[cfg(any(feature = "toml", feature = "yaml"))] + ErrorInner::MissingFileExtension { .. } => None, + #[cfg(any(feature = "toml", feature = "yaml"))] + ErrorInner::MissingRequiredFile { .. } => None, } } } @@ -75,18 +89,22 @@ impl fmt::Display for Error { ErrorInner::MissingValue(path) => { std::write!(f, "required configuration value is missing: '{}'", path) } + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::Io { path: Some(path), .. } => { std::write!(f, "IO error occured while reading configuration file '{}'", path.display(), ) } + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::Io { path: None, .. } => { std::write!(f, "IO error occured while loading configuration") } + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::Deserialization { source: Some(source), .. } => { std::write!(f, "failed to deserialize configuration from {}", source) } + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::Deserialization { source: None, .. } => { std::write!(f, "failed to deserialize configuration") } @@ -98,18 +116,21 @@ impl fmt::Display for Error { msg, ) } + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::UnsupportedFileFormat { path } => { std::write!(f, "unknown configuration file format/extension: '{}'", path.display(), ) } + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::MissingFileExtension { path } => { std::write!(f, "cannot guess configuration file format due to missing file extension in '{}'", path.display(), ) } + #[cfg(any(feature = "toml", feature = "yaml"))] ErrorInner::MissingRequiredFile { path } => { std::write!(f, "required configuration file does not exist: '{}'", diff --git a/src/lib.rs b/src/lib.rs index 16a1c47..94d8be5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,9 +88,11 @@ //! # #[derive(Config)] //! # struct Conf {} //! // Load from a single file only. +//! # #[cfg(feature = "toml")] //! let config = Conf::from_file("config.toml")?; //! //! // Or load from multiple sources (higher priority sources are listed first). +//! # #[cfg(feature = "toml")] //! let config = Conf::builder() //! .env() //! .file("config.toml") @@ -113,7 +115,10 @@ //! [`Partial::default_values`] as the last layer. //! //! ```rust,no_run +//! # #[cfg(feature = "toml")] //! use confique::{Config, File, FileFormat, Partial}; +//! # #[cfg(not(feature = "toml"))] +//! # use confique::{Config, Partial}; //! //! #[derive(Config)] //! struct Conf { @@ -121,9 +126,12 @@ //! } //! //! type PartialConf = ::Partial; +//! # #[cfg(feature = "toml")] //! let from_file: PartialConf = File::with_format("/etc/foo/config", FileFormat::Toml) //! .required() //! .load()?; +//! # #[cfg(not(feature = "toml"))] +//! let from_file: PartialConf = todo!(); //! let manual = PartialConf { //! // Remember: all fields in the partial types are `Option`s! //! foo: Some(3.14), @@ -149,6 +157,7 @@ //! - `toml`: enables TOML support and adds the `toml` dependency. //! - `yaml`: enables YAML support and adds the `serde_yaml` dependency. +#[cfg(any(feature = "toml", feature = "yaml"))] use std::path::PathBuf; use serde::Deserialize; @@ -159,12 +168,14 @@ pub mod internal; mod builder; mod env; mod error; -mod file; pub mod meta; #[cfg(any(feature = "toml", feature = "yaml"))] mod format; +#[cfg(any(feature = "toml", feature = "yaml"))] +mod file; + #[cfg(feature = "toml")] pub mod toml; @@ -176,9 +187,11 @@ pub use serde; pub use self::{ builder::Builder, error::Error, - file::{File, FileFormat}, }; +#[cfg(any(feature = "toml", feature = "yaml"))] +pub use crate::file::{File, FileFormat}; + /// Derives (automatically implements) [`Config`] for a struct. /// @@ -367,6 +380,7 @@ pub trait Config: Sized { /// port: u16, /// } /// + /// #[cfg(feature = "toml")] /// let conf = Conf::builder() /// .env() /// .file("app.toml") @@ -400,6 +414,7 @@ pub trait Config: Sized { /// /// let conf = Conf::from_file("config.toml"); /// ``` + #[cfg(any(feature = "toml", feature = "yaml"))] fn from_file(path: impl Into) -> Result { let default_values = Self::Partial::default_values(); let mut file = File::new(path)?;