Make serde optional

This commit is contained in:
Kaleb Elwert 2020-01-28 17:58:38 -08:00
parent 2339ca5fe6
commit dd09555537
5 changed files with 81 additions and 74 deletions

View file

@ -2,7 +2,7 @@ language: rust
rust: stable rust: stable
sudo: false sudo: false
script: script:
- cargo test --all --features "toml yaml json" - cargo test --all --features "toml_config yaml_config json_config"
- cargo build - cargo build
# No idea how to fix this, since we don't depend on futures-preview directly. # No idea how to fix this, since we don't depend on futures-preview directly.
# - rustdoc --test README.md --extern irc=target/debug/libirc.rlib -L target/debug/deps --edition 2018 # - rustdoc --test README.md --extern irc=target/debug/libirc.rlib -L target/debug/deps --edition 2018

View file

@ -20,11 +20,16 @@ is-it-maintained-open-issues = { repository = "aatxe/irc" }
members = [ "./", "irc-proto" ] members = [ "./", "irc-proto" ]
[features] [features]
default = ["ctcp", "toml"] default = ["ctcp", "toml_config"]
ctcp = [] ctcp = []
nochanlists = [] nochanlists = []
json = ["serde_json"] json_config = ["serde", "serde_derive", "serde_json"]
yaml = ["serde_yaml"] toml_config = ["serde", "serde_derive", "toml"]
yaml_config = ["serde", "serde_derive", "serde_yaml"]
# Temporary transitionary features
json = ["json_config"]
yaml = ["yaml_config"]
[dependencies] [dependencies]
thiserror = "1.0.2" thiserror = "1.0.2"
@ -35,8 +40,8 @@ encoding = "0.2"
irc-proto = { version = "*", path = "irc-proto" } irc-proto = { version = "*", path = "irc-proto" }
log = "0.4" log = "0.4"
native-tls = "0.2" native-tls = "0.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"], optional = true }
serde_derive = "1.0" serde_derive = { version = "1.0", optional = true }
tokio = { version = "0.2.4", features = ["time", "net", "stream", "macros", "stream"] } tokio = { version = "0.2.4", features = ["time", "net", "stream", "macros", "stream"] }
tokio-util = { version = "0.2.0", features = ["codec"] } tokio-util = { version = "0.2.0", features = ["codec"] }
tokio-tls = "0.3.0" tokio-tls = "0.3.0"

View file

@ -86,9 +86,9 @@ programmatic configuration. Runtime loading is done via the function `Config::lo
sufficient for most IRC bots. Programmatic configuration is convenient for writing tests, but can sufficient for most IRC bots. Programmatic configuration is convenient for writing tests, but can
also be useful when defining your own custom configuration format that can be converted to `Config`. also be useful when defining your own custom configuration format that can be converted to `Config`.
The primary configuration format is TOML, but if you are so inclined, you can use JSON and/or YAML The primary configuration format is TOML, but if you are so inclined, you can use JSON and/or YAML
via the optional `json` and `yaml` features respectively. At the minimum, a configuration requires via the optional `json_config` and `yaml_config` features respectively. At the minimum, a configuration
`nickname` and `server` to be defined, and all other fields are optional. You can find detailed requires `nickname` and `server` to be defined, and all other fields are optional. You can find
explanations of the various fields on [docs.rs][config-fields]. detailed explanations of the various fields on [docs.rs][config-fields].
[config-fields]: https://docs.rs/irc/*/irc/client/data/config/struct.Config.html#fields [config-fields]: https://docs.rs/irc/*/irc/client/data/config/struct.Config.html#fields

View file

@ -1,4 +1,5 @@
//! JSON configuration files using serde //! JSON configuration files using serde
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashMap, collections::HashMap,
@ -8,15 +9,15 @@ use std::{
}; };
use tokio::net::ToSocketAddrs; use tokio::net::ToSocketAddrs;
#[cfg(feature = "json")] #[cfg(feature = "json_config")]
use serde_json; use serde_json;
#[cfg(feature = "yaml")] #[cfg(feature = "yaml_config")]
use serde_yaml; use serde_yaml;
#[cfg(feature = "toml")] #[cfg(feature = "toml_config")]
use toml; use toml;
use crate::error::Error::InvalidConfig; use crate::error::Error::InvalidConfig;
#[cfg(feature = "toml")] #[cfg(feature = "toml_config")]
use crate::error::TomlError; use crate::error::TomlError;
use crate::error::{ConfigError, Result}; use crate::error::{ConfigError, Result};
@ -65,127 +66,129 @@ use crate::error::{ConfigError, Result};
/// let config = Config::load("config.toml").unwrap(); /// let config = Config::load("config.toml").unwrap();
/// # } /// # }
/// ``` /// ```
#[derive(Clone, Deserialize, Serialize, Default, PartialEq, Debug)] #[derive(Clone, Default, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Config { pub struct Config {
/// A list of the owners of the client by nickname (for bots). /// A list of the owners of the client by nickname (for bots).
#[serde(skip_serializing_if = "Vec::is_empty")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub owners: Vec<String>, pub owners: Vec<String>,
/// The client's nickname. /// The client's nickname.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub nickname: Option<String>, pub nickname: Option<String>,
/// The client's NICKSERV password. /// The client's NICKSERV password.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub nick_password: Option<String>, pub nick_password: Option<String>,
/// Alternative nicknames for the client, if the default is taken. /// Alternative nicknames for the client, if the default is taken.
#[serde(skip_serializing_if = "Vec::is_empty")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub alt_nicks: Vec<String>, pub alt_nicks: Vec<String>,
/// The client's username. /// The client's username.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub username: Option<String>, pub username: Option<String>,
/// The client's real name. /// The client's real name.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub realname: Option<String>, pub realname: Option<String>,
/// The server to connect to. /// The server to connect to.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub server: Option<String>, pub server: Option<String>,
/// The port to connect on. /// The port to connect on.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub port: Option<u16>, pub port: Option<u16>,
/// The password to connect to the server. /// The password to connect to the server.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub password: Option<String>, pub password: Option<String>,
/// Whether or not to use SSL. /// Whether or not to use SSL.
/// Clients will automatically panic if this is enabled without SSL support. /// Clients will automatically panic if this is enabled without SSL support.
#[serde(skip_serializing_if = "is_false")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub use_ssl: bool, pub use_ssl: bool,
/// The path to the SSL certificate for this server in DER format. /// The path to the SSL certificate for this server in DER format.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub cert_path: Option<String>, pub cert_path: Option<String>,
/// The path to a SSL certificate to use for CertFP client authentication in DER format. /// The path to a SSL certificate to use for CertFP client authentication in DER format.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub client_cert_path: Option<String>, pub client_cert_path: Option<String>,
/// The password for the certificate to use in CertFP authentication. /// The password for the certificate to use in CertFP authentication.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub client_cert_pass: Option<String>, pub client_cert_pass: Option<String>,
/// The encoding type used for this connection. /// The encoding type used for this connection.
/// This is typically UTF-8, but could be something else. /// This is typically UTF-8, but could be something else.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub encoding: Option<String>, pub encoding: Option<String>,
/// A list of channels to join on connection. /// A list of channels to join on connection.
#[serde(skip_serializing_if = "Vec::is_empty")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub channels: Vec<String>, pub channels: Vec<String>,
/// User modes to set on connect. Example: "+RB -x" /// User modes to set on connect. Example: "+RB -x"
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub umodes: Option<String>, pub umodes: Option<String>,
/// The text that'll be sent in response to CTCP USERINFO requests. /// The text that'll be sent in response to CTCP USERINFO requests.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub user_info: Option<String>, pub user_info: Option<String>,
/// The text that'll be sent in response to CTCP VERSION requests. /// The text that'll be sent in response to CTCP VERSION requests.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub version: Option<String>, pub version: Option<String>,
/// The text that'll be sent in response to CTCP SOURCE requests. /// The text that'll be sent in response to CTCP SOURCE requests.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub source: Option<String>, pub source: Option<String>,
/// The amount of inactivity in seconds before the client will ping the server. /// The amount of inactivity in seconds before the client will ping the server.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub ping_time: Option<u32>, pub ping_time: Option<u32>,
/// The amount of time in seconds for a client to reconnect due to no ping response. /// The amount of time in seconds for a client to reconnect due to no ping response.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub ping_timeout: Option<u32>, pub ping_timeout: Option<u32>,
/// The length in seconds of a rolling window for message throttling. If more than /// The length in seconds of a rolling window for message throttling. If more than
/// `max_messages_in_burst` messages are sent within `burst_window_length` seconds, additional /// `max_messages_in_burst` messages are sent within `burst_window_length` seconds, additional
/// messages will be delayed automatically as appropriate. In particular, in the past /// messages will be delayed automatically as appropriate. In particular, in the past
/// `burst_window_length` seconds, there will never be more than `max_messages_in_burst` messages /// `burst_window_length` seconds, there will never be more than `max_messages_in_burst` messages
/// sent. /// sent.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub burst_window_length: Option<u32>, pub burst_window_length: Option<u32>,
/// The maximum number of messages that can be sent in a burst window before they'll be delayed. /// The maximum number of messages that can be sent in a burst window before they'll be delayed.
/// Messages are automatically delayed as appropriate. /// Messages are automatically delayed as appropriate.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub max_messages_in_burst: Option<u32>, pub max_messages_in_burst: Option<u32>,
/// Whether the client should use NickServ GHOST to reclaim its primary nickname if it is in /// Whether the client should use NickServ GHOST to reclaim its primary nickname if it is in
/// use. This has no effect if `nick_password` is not set. /// use. This has no effect if `nick_password` is not set.
#[serde(skip_serializing_if = "is_false")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub should_ghost: bool, pub should_ghost: bool,
/// The command(s) that should be sent to NickServ to recover a nickname. The nickname and /// The command(s) that should be sent to NickServ to recover a nickname. The nickname and
/// password will be appended in that order after the command. /// password will be appended in that order after the command.
/// E.g. `["RECOVER", "RELEASE"]` means `RECOVER nick pass` and `RELEASE nick pass` will be sent /// E.g. `["RECOVER", "RELEASE"]` means `RECOVER nick pass` and `RELEASE nick pass` will be sent
/// in that order. /// in that order.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub ghost_sequence: Option<Vec<String>>, pub ghost_sequence: Option<Vec<String>>,
/// Whether or not to use a fake connection for testing purposes. You probably will never want /// Whether or not to use a fake connection for testing purposes. You probably will never want
/// to enable this, but it is used in unit testing for the `irc` crate. /// to enable this, but it is used in unit testing for the `irc` crate.
#[serde(skip_serializing_if = "is_false")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub use_mock_connection: bool, pub use_mock_connection: bool,
/// The initial value used by the fake connection for testing. You probably will never need to /// The initial value used by the fake connection for testing. You probably will never need to
/// set this, but it is used in unit testing for the `irc` crate. /// set this, but it is used in unit testing for the `irc` crate.
#[serde(skip_serializing_if = "Option::is_none")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub mock_initial_value: Option<String>, pub mock_initial_value: Option<String>,
/// A mapping of channel names to keys for join-on-connect. /// A mapping of channel names to keys for join-on-connect.
#[serde(skip_serializing_if = "HashMap::is_empty")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "HashMap::is_empty"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub channel_keys: HashMap<String, String>, pub channel_keys: HashMap<String, String>,
/// A map of additional options to be stored in config. /// A map of additional options to be stored in config.
#[serde(skip_serializing_if = "HashMap::is_empty")] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "HashMap::is_empty"))]
#[serde(default)] #[cfg_attr(feature = "serde", serde(default))]
pub options: HashMap<String, String>, pub options: HashMap<String, String>,
/// The path that this configuration was loaded from. /// The path that this configuration was loaded from.
/// ///
/// This should not be specified in any configuration. It will automatically be handled by the library. /// This should not be specified in any configuration. It will automatically be handled by the library.
#[serde(skip_serializing)] #[cfg_attr(feature = "serde", serde(skip_serializing))]
#[doc(hidden)] #[doc(hidden)]
pub path: Option<PathBuf>, pub path: Option<PathBuf>,
} }
#[cfg(feature = "serde")]
fn is_false(v: &bool) -> bool { fn is_false(v: &bool) -> bool {
!v !v
} }
@ -230,7 +233,7 @@ impl Config {
res.map(|config| config.with_path(path)) res.map(|config| config.with_path(path))
} }
#[cfg(feature = "json")] #[cfg(feature = "json_config")]
fn load_json<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> { fn load_json<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> {
serde_json::from_str(data).map_err(|e| InvalidConfig { serde_json::from_str(data).map_err(|e| InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -238,7 +241,7 @@ impl Config {
}) })
} }
#[cfg(not(feature = "json"))] #[cfg(not(feature = "json_config"))]
fn load_json<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> { fn load_json<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> {
Err(InvalidConfig { Err(InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -246,7 +249,7 @@ impl Config {
}) })
} }
#[cfg(feature = "toml")] #[cfg(feature = "toml_config")]
fn load_toml<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> { fn load_toml<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> {
toml::from_str(data).map_err(|e| InvalidConfig { toml::from_str(data).map_err(|e| InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -254,7 +257,7 @@ impl Config {
}) })
} }
#[cfg(not(feature = "toml"))] #[cfg(not(feature = "toml_config"))]
fn load_toml<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> { fn load_toml<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> {
Err(InvalidConfig { Err(InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -262,7 +265,7 @@ impl Config {
}) })
} }
#[cfg(feature = "yaml")] #[cfg(feature = "yaml_config")]
fn load_yaml<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> { fn load_yaml<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> {
serde_yaml::from_str(data).map_err(|e| InvalidConfig { serde_yaml::from_str(data).map_err(|e| InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -270,7 +273,7 @@ impl Config {
}) })
} }
#[cfg(not(feature = "yaml"))] #[cfg(not(feature = "yaml_config"))]
fn load_yaml<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> { fn load_yaml<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> {
Err(InvalidConfig { Err(InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -308,7 +311,7 @@ impl Config {
Ok(()) Ok(())
} }
#[cfg(feature = "json")] #[cfg(feature = "json_config")]
fn save_json<P: AsRef<Path>>(&self, path: &P) -> Result<String> { fn save_json<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
serde_json::to_string(self).map_err(|e| InvalidConfig { serde_json::to_string(self).map_err(|e| InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -316,7 +319,7 @@ impl Config {
}) })
} }
#[cfg(not(feature = "json"))] #[cfg(not(feature = "json_config"))]
fn save_json<P: AsRef<Path>>(&self, path: &P) -> Result<String> { fn save_json<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
Err(InvalidConfig { Err(InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -324,7 +327,7 @@ impl Config {
}) })
} }
#[cfg(feature = "toml")] #[cfg(feature = "toml_config")]
fn save_toml<P: AsRef<Path>>(&self, path: &P) -> Result<String> { fn save_toml<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
toml::to_string(self).map_err(|e| InvalidConfig { toml::to_string(self).map_err(|e| InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -332,7 +335,7 @@ impl Config {
}) })
} }
#[cfg(not(feature = "toml"))] #[cfg(not(feature = "toml_config"))]
fn save_toml<P: AsRef<Path>>(&self, path: &P) -> Result<String> { fn save_toml<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
Err(InvalidConfig { Err(InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -340,7 +343,7 @@ impl Config {
}) })
} }
#[cfg(feature = "yaml")] #[cfg(feature = "yaml_config")]
fn save_yaml<P: AsRef<Path>>(&self, path: &P) -> Result<String> { fn save_yaml<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
serde_yaml::to_string(self).map_err(|e| InvalidConfig { serde_yaml::to_string(self).map_err(|e| InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -348,7 +351,7 @@ impl Config {
}) })
} }
#[cfg(not(feature = "yaml"))] #[cfg(not(feature = "yaml_config"))]
fn save_yaml<P: AsRef<Path>>(&self, path: &P) -> Result<String> { fn save_yaml<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
Err(InvalidConfig { Err(InvalidConfig {
path: path.as_ref().to_string_lossy().into_owned(), path: path.as_ref().to_string_lossy().into_owned(),
@ -561,8 +564,7 @@ impl Config {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::Config; use super::{Config, Result};
use anyhow::Result;
use std::collections::HashMap; use std::collections::HashMap;
#[allow(unused)] #[allow(unused)]
@ -609,7 +611,7 @@ mod test {
} }
#[test] #[test]
#[cfg(feature = "json")] #[cfg(feature = "json_config")]
fn load_from_json() -> Result<()> { fn load_from_json() -> Result<()> {
const DATA: &str = include_str!("client_config.json"); const DATA: &str = include_str!("client_config.json");
assert_eq!( assert_eq!(
@ -620,7 +622,7 @@ mod test {
} }
#[test] #[test]
#[cfg(feature = "toml")] #[cfg(feature = "toml_config")]
fn load_from_toml() -> Result<()> { fn load_from_toml() -> Result<()> {
const DATA: &str = include_str!("client_config.toml"); const DATA: &str = include_str!("client_config.toml");
assert_eq!( assert_eq!(
@ -631,7 +633,7 @@ mod test {
} }
#[test] #[test]
#[cfg(feature = "yaml")] #[cfg(feature = "yaml_config")]
fn load_from_yaml() -> Result<()> { fn load_from_yaml() -> Result<()> {
const DATA: &str = include_str!("client_config.yaml"); const DATA: &str = include_str!("client_config.yaml");
assert_eq!( assert_eq!(

View file

@ -94,17 +94,17 @@ pub enum Error {
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum ConfigError { pub enum ConfigError {
/// Failed to parse as TOML. /// Failed to parse as TOML.
#[cfg(feature = "toml")] #[cfg(feature = "toml_config")]
#[error("invalid toml")] #[error("invalid toml")]
InvalidToml(#[source] TomlError), InvalidToml(#[source] TomlError),
/// Failed to parse as JSON. /// Failed to parse as JSON.
#[cfg(feature = "json")] #[cfg(feature = "json_config")]
#[error("invalid json")] #[error("invalid json")]
InvalidJson(#[source] serde_json::Error), InvalidJson(#[source] serde_json::Error),
/// Failed to parse as YAML. /// Failed to parse as YAML.
#[cfg(feature = "yaml")] #[cfg(feature = "yaml_config")]
#[error("invalid yaml")] #[error("invalid yaml")]
InvalidYaml(#[source] serde_yaml::Error), InvalidYaml(#[source] serde_yaml::Error),
@ -136,7 +136,7 @@ pub enum ConfigError {
} }
/// A wrapper that combines toml's serialization and deserialization errors. /// A wrapper that combines toml's serialization and deserialization errors.
#[cfg(feature = "toml")] #[cfg(feature = "toml_config")]
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum TomlError { pub enum TomlError {
/// A TOML deserialization error. /// A TOML deserialization error.