diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..2e82d7e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,106 @@ +use std::{ffi::OsString, fmt, path::PathBuf}; + + +/// Type describing all errors that can occur in this library. +pub struct Error { + pub(crate) inner: Box, +} + +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 + /// human-readable path to the value, e.g. `http.port`. + MissingValue(String), + + /// An IO error occured, e.g. when reading a file. + Io { + path: Option, + err: std::io::Error, + }, + + /// Returned by `Source::load` implementations when deserialization fails. + Deserialization { + /// A human readable description for the error message, describing from + /// what source it was attempted to deserialize. Completes the sentence + /// "failed to deserialize configuration from ". E.g. "file 'foo.toml'" + /// or "environment variable 'FOO_PORT'". + source: Option, + err: Box, + }, + + /// 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. + UnsupportedFileFormat { + path: PathBuf, + extension: OsString, + }, + + /// Returned by the [`Source`] impls for `Path` and `PathBuf` if the path + /// does not contain a file extension. + MissingFileExtension { + path: PathBuf, + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match &*self.inner { + ErrorInner::Io { err, .. } => Some(err), + ErrorInner::Deserialization { err, .. } => Some(&**err), + ErrorInner::MissingValue(_) + | ErrorInner::UnsupportedFileFormat { .. } + | ErrorInner::MissingFileExtension { .. } => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &*self.inner { + ErrorInner::MissingValue(path) => { + std::write!(f, "required configuration value is missing: '{}'", path) + } + ErrorInner::Io { path: Some(path), .. } => { + std::write!(f, + "IO error occured while reading configuration file '{}'", + path.display(), + ) + } + ErrorInner::Io { path: None, .. } => { + std::write!(f, "IO error occured while loading configuration") + } + ErrorInner::Deserialization { source: Some(source), .. } => { + std::write!(f, "failed to deserialize configuration from {}", source) + } + ErrorInner::Deserialization { source: None, .. } => { + std::write!(f, "failed to deserialize configuration") + } + ErrorInner::UnsupportedFileFormat { path, extension } => { + std::write!(f, + "unknown configuration file format '{}' of '{}'", + extension.to_string_lossy(), + path.display(), + ) + } + ErrorInner::MissingFileExtension { path } => { + std::write!(f, + "cannot guess configuration file format due to missing file extension in '{}'", + path.display(), + ) + } + } + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl From for Error { + fn from(inner: ErrorInner) -> Self { + Self { inner: Box::new(inner) } + } +} diff --git a/src/file.rs b/src/file.rs index 524c03b..93829c1 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,6 +1,6 @@ use std::{ffi::OsStr, fs, io, path::{Path, PathBuf}}; -use crate::{Config, Error, ErrorInner, Partial, Source}; +use crate::{Config, Error, Partial, Source, error::ErrorInner}; impl Source for &Path { diff --git a/src/internal.rs b/src/internal.rs index 734810e..3ee4e75 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -2,7 +2,7 @@ //! intended to be used directly. None of this is covered by semver! Do not use //! any of this directly. -use crate::{Error, ErrorInner}; +use crate::{Error, error::ErrorInner}; pub fn deserialize_default(src: I) -> Result where diff --git a/src/lib.rs b/src/lib.rs index fa39e34..83522fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,19 @@ -use std::{ffi::OsString, fmt, path::PathBuf}; - use serde::Deserialize; #[doc(hidden)] pub mod internal; +mod error; mod file; pub use serde; pub use confique_macro::Config; -pub use self::file::{File, FileFormat}; +pub use self::{ + error::Error, + file::{File, FileFormat}, +}; + /// A configuration object that can be deserialized in layers via `serde`. @@ -83,107 +86,3 @@ pub trait Source { /// Attempts to load a potentially partially configuration object. fn load(&self) -> Result; } - -/// Type describing all errors that can occur in this library. -pub struct Error { - inner: Box, -} - -enum ErrorInner { - /// Returned by `Config::from_partial` when the partial does not contain - /// values for all required configuration values. The string is a - /// human-readable path to the value, e.g. `http.port`. - MissingValue(String), - - /// An IO error occured, e.g. when reading a file. - Io { - path: Option, - err: std::io::Error, - }, - - /// Returned by `Source::load` implementations when deserialization fails. - Deserialization { - /// A human readable description for the error message, describing from - /// what source it was attempted to deserialize. Completes the sentence - /// "failed to deserialize configuration from ". E.g. "file 'foo.toml'" - /// or "environment variable 'FOO_PORT'". - source: Option, - err: Box, - }, - - /// 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. - UnsupportedFileFormat { - path: PathBuf, - extension: OsString, - }, - - /// Returned by the [`Source`] impls for `Path` and `PathBuf` if the path - /// does not contain a file extension. - MissingFileExtension { - path: PathBuf, - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match &*self.inner { - ErrorInner::Io { err, .. } => Some(err), - ErrorInner::Deserialization { err, .. } => Some(&**err), - ErrorInner::MissingValue(_) - | ErrorInner::UnsupportedFileFormat { .. } - | ErrorInner::MissingFileExtension { .. } => None, - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match &*self.inner { - ErrorInner::MissingValue(path) => { - std::write!(f, "required configuration value is missing: '{}'", path) - } - ErrorInner::Io { path: Some(path), .. } => { - std::write!(f, - "IO error occured while reading configuration file '{}'", - path.display(), - ) - } - ErrorInner::Io { path: None, .. } => { - std::write!(f, "IO error occured while loading configuration") - } - ErrorInner::Deserialization { source: Some(source), .. } => { - std::write!(f, "failed to deserialize configuration from {}", source) - } - ErrorInner::Deserialization { source: None, .. } => { - std::write!(f, "failed to deserialize configuration") - } - ErrorInner::UnsupportedFileFormat { path, extension } => { - std::write!(f, - "unknown configuration file format '{}' of '{}'", - extension.to_string_lossy(), - path.display(), - ) - } - ErrorInner::MissingFileExtension { path } => { - std::write!(f, - "cannot guess configuration file format due to missing file extension in '{}'", - path.display(), - ) - } - } - } -} - -impl fmt::Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } -} - -impl From for Error { - fn from(inner: ErrorInner) -> Self { - Self { inner: Box::new(inner) } - } -}