diff --git a/src/error.rs b/src/error.rs index 1200308..6d85f39 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,5 @@ use std::fmt; -#[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] use std::path::PathBuf; @@ -10,6 +9,11 @@ pub struct Error { pub(crate) inner: Box, } +// If all these features are disabled, lots of these errors are unused. But +// instead of repeating this cfg-attribute a lot in the rest of the file, we +// just live with these unused variants. It's not like we need to optimize the +// size of `ErrorInner`. +#[cfg_attr(not(any(feature = "toml", feature = "yaml", feature = "json5")), allow(dead_code))] pub(crate) enum ErrorInner { /// Returned by `Config::from_partial` when the partial does not contain /// values for all required configuration values. The string is a @@ -17,14 +21,12 @@ pub(crate) enum ErrorInner { MissingValue(String), /// An IO error occured, e.g. when reading a file. - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] Io { path: Option, err: std::io::Error, }, /// Returned by `Source::load` implementations when deserialization fails. - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] Deserialization { /// A human readable description for the error message, describing from /// what source it was attempted to deserialize. Completes the sentence @@ -51,20 +53,17 @@ 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", feature = "json5"))] 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", feature = "json5"))] MissingFileExtension { path: PathBuf, }, /// A file source was marked as required but the file does not exist. - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] MissingRequiredFile { path: PathBuf, } @@ -73,18 +72,13 @@ 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", feature = "json5"))] ErrorInner::Io { err, .. } => Some(err), - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::Deserialization { err, .. } => Some(&**err), ErrorInner::MissingValue(_) => None, ErrorInner::EnvNotUnicode { .. } => None, ErrorInner::EnvDeserialization { .. } => None, - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::UnsupportedFileFormat { .. } => None, - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::MissingFileExtension { .. } => None, - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::MissingRequiredFile { .. } => None, } } @@ -96,22 +90,18 @@ impl fmt::Display for Error { ErrorInner::MissingValue(path) => { std::write!(f, "required configuration value is missing: '{path}'") } - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::Io { path: Some(path), .. } => { std::write!(f, "IO error occured while reading configuration file '{}'", path.display(), ) } - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::Io { path: None, .. } => { std::write!(f, "IO error occured while loading configuration") } - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::Deserialization { source: Some(source), .. } => { std::write!(f, "failed to deserialize configuration from {source}") } - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::Deserialization { source: None, .. } => { std::write!(f, "failed to deserialize configuration") } @@ -123,21 +113,18 @@ impl fmt::Display for Error { std::write!(f, "failed to deserialize value `{field}` from \ environment variable `{key}`: {msg}") } - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] ErrorInner::UnsupportedFileFormat { path } => { std::write!(f, "unknown configuration file format/extension: '{}'", path.display(), ) } - #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] 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", feature = "json5"))] ErrorInner::MissingRequiredFile { path } => { std::write!(f, "required configuration file does not exist: '{}'", diff --git a/src/file.rs b/src/file.rs index eb8a365..5a3f7c2 100644 --- a/src/file.rs +++ b/src/file.rs @@ -49,14 +49,6 @@ impl File { /// Attempts to load the file into the partial configuration `P`. pub fn load(&self) -> Result { - // Unfortunately, if no file format is enabled, this emits unused variable - // warnings. This should not happen as `self`, a type containing an empty - // enum, is in scope, meaning that the code cannot be reached. - #![cfg_attr( - not(any(feature = "toml", feature = "yaml", feature = "json5")), - allow(unused_variables), - )] - // Load file contents. If the file does not exist and was not marked as // required, we just return an empty layer. let file_content = match fs::read(&self.path) { diff --git a/src/lib.rs b/src/lib.rs index f78ad42..fc9b520 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ //! //! Small example: //! -//! ```rust +//! ``` //! use confique::Config; //! //! #[derive(Config)] @@ -43,7 +43,7 @@ //! are somewhat structured or have sections. You can do that by including //! other types that implement `Config` with `#[config(nested)]`. //! -//! ```rust +//! ``` //! use std::path::PathBuf; //! use confique::Config; //! @@ -84,7 +84,7 @@ //! provided high-level methods of [`Config`], like [`Config::from_file`] and //! [`Config::builder`]. //! -//! ```rust +//! ``` //! use confique::Config; //! //! # #[derive(Config)] @@ -118,11 +118,12 @@ //! type via [`Config::from_partial`]. And you probably also want to use //! [`Partial::default_values`] as the last layer. //! -//! ```rust,no_run -//! # #[cfg(feature = "toml")] -//! use confique::{Config, File, FileFormat, Partial}; +//! ```no_run //! # #[cfg(not(feature = "toml"))] -//! # use confique::{Config, Partial}; +//! # fn main() {} +//! # #[cfg(feature = "toml")] +//! # fn main() -> Result<(), confique::Error> { +//! use confique::{Config, File, FileFormat, Partial}; //! //! #[derive(Config)] //! struct Conf { @@ -130,11 +131,9 @@ //! } //! //! 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! @@ -144,7 +143,8 @@ //! //! let merged = from_file.with_fallback(manual).with_fallback(defaults); //! let config = Conf::from_partial(merged)?; -//! # Ok::<_, confique::Error>(()) +//! # Ok(()) +//! # } //! ``` //! //! ## Using your configuration @@ -162,9 +162,6 @@ //! - `yaml`: enables YAML support and adds the `serde_yaml` dependency. //! - `json5`: enables YAML support and adds the `json5` dependency. -#[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] -use std::path::PathBuf; - use serde::Deserialize; #[doc(hidden)] @@ -430,7 +427,7 @@ pub trait Config: Sized { /// let conf = Conf::from_file("config.toml"); /// ``` #[cfg(any(feature = "toml", feature = "yaml", feature = "json5"))] - fn from_file(path: impl Into) -> Result { + fn from_file(path: impl Into) -> Result { let default_values = Self::Partial::default_values(); let mut file = File::new(path)?; if !default_values.is_complete() {