From dd09555537c2663fc208286f02d312656e027e61 Mon Sep 17 00:00:00 2001 From: Kaleb Elwert Date: Tue, 28 Jan 2020 17:58:38 -0800 Subject: [PATCH] Make serde optional --- .travis.yml | 2 +- Cargo.toml | 15 +++-- README.md | 6 +- src/client/data/config.rs | 124 +++++++++++++++++++------------------- src/error.rs | 8 +-- 5 files changed, 81 insertions(+), 74 deletions(-) diff --git a/.travis.yml b/.travis.yml index 62e3949..2ef5006 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: rust rust: stable sudo: false script: - - cargo test --all --features "toml yaml json" + - cargo test --all --features "toml_config yaml_config json_config" - cargo build # 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 diff --git a/Cargo.toml b/Cargo.toml index 19ddf41..bc63221 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,11 +20,16 @@ is-it-maintained-open-issues = { repository = "aatxe/irc" } members = [ "./", "irc-proto" ] [features] -default = ["ctcp", "toml"] +default = ["ctcp", "toml_config"] ctcp = [] nochanlists = [] -json = ["serde_json"] -yaml = ["serde_yaml"] +json_config = ["serde", "serde_derive", "serde_json"] +toml_config = ["serde", "serde_derive", "toml"] +yaml_config = ["serde", "serde_derive", "serde_yaml"] + +# Temporary transitionary features +json = ["json_config"] +yaml = ["yaml_config"] [dependencies] thiserror = "1.0.2" @@ -35,8 +40,8 @@ encoding = "0.2" irc-proto = { version = "*", path = "irc-proto" } log = "0.4" native-tls = "0.2" -serde = { version = "1.0", features = ["derive"] } -serde_derive = "1.0" +serde = { version = "1.0", features = ["derive"], optional = true } +serde_derive = { version = "1.0", optional = true } tokio = { version = "0.2.4", features = ["time", "net", "stream", "macros", "stream"] } tokio-util = { version = "0.2.0", features = ["codec"] } tokio-tls = "0.3.0" diff --git a/README.md b/README.md index 90510b2..c1d8e26 100644 --- a/README.md +++ b/README.md @@ -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 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 -via the optional `json` and `yaml` features respectively. At the minimum, a configuration requires -`nickname` and `server` to be defined, and all other fields are optional. You can find detailed -explanations of the various fields on [docs.rs][config-fields]. +via the optional `json_config` and `yaml_config` features respectively. At the minimum, a configuration +requires `nickname` and `server` to be defined, and all other fields are optional. You can find +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 diff --git a/src/client/data/config.rs b/src/client/data/config.rs index 76a34a0..1e09175 100644 --- a/src/client/data/config.rs +++ b/src/client/data/config.rs @@ -1,4 +1,5 @@ //! JSON configuration files using serde +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -8,15 +9,15 @@ use std::{ }; use tokio::net::ToSocketAddrs; -#[cfg(feature = "json")] +#[cfg(feature = "json_config")] use serde_json; -#[cfg(feature = "yaml")] +#[cfg(feature = "yaml_config")] use serde_yaml; -#[cfg(feature = "toml")] +#[cfg(feature = "toml_config")] use toml; use crate::error::Error::InvalidConfig; -#[cfg(feature = "toml")] +#[cfg(feature = "toml_config")] use crate::error::TomlError; use crate::error::{ConfigError, Result}; @@ -65,127 +66,129 @@ use crate::error::{ConfigError, Result}; /// 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 { /// A list of the owners of the client by nickname (for bots). - #[serde(skip_serializing_if = "Vec::is_empty")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] + #[cfg_attr(feature = "serde", serde(default))] pub owners: Vec, /// 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, /// 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, /// Alternative nicknames for the client, if the default is taken. - #[serde(skip_serializing_if = "Vec::is_empty")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] + #[cfg_attr(feature = "serde", serde(default))] pub alt_nicks: Vec, /// 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, /// 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, /// 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, /// 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, /// 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, /// Whether or not to use SSL. /// Clients will automatically panic if this is enabled without SSL support. - #[serde(skip_serializing_if = "is_false")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))] + #[cfg_attr(feature = "serde", serde(default))] pub use_ssl: bool, /// 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, /// 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, /// 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, /// The encoding type used for this connection. /// 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, /// A list of channels to join on connection. - #[serde(skip_serializing_if = "Vec::is_empty")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] + #[cfg_attr(feature = "serde", serde(default))] pub channels: Vec, /// 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, /// 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, /// 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, /// 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, /// 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, /// 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, /// 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 /// 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 /// sent. - #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub burst_window_length: Option, /// The maximum number of messages that can be sent in a burst window before they'll be delayed. /// 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, /// 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. - #[serde(skip_serializing_if = "is_false")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))] + #[cfg_attr(feature = "serde", serde(default))] pub should_ghost: bool, /// 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. /// E.g. `["RECOVER", "RELEASE"]` means `RECOVER nick pass` and `RELEASE nick pass` will be sent /// in that order. - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + #[cfg_attr(feature = "serde", serde(default))] pub ghost_sequence: Option>, /// 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. - #[serde(skip_serializing_if = "is_false")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false"))] + #[cfg_attr(feature = "serde", serde(default))] pub use_mock_connection: bool, /// 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. - #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub mock_initial_value: Option, /// A mapping of channel names to keys for join-on-connect. - #[serde(skip_serializing_if = "HashMap::is_empty")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "HashMap::is_empty"))] + #[cfg_attr(feature = "serde", serde(default))] pub channel_keys: HashMap, /// A map of additional options to be stored in config. - #[serde(skip_serializing_if = "HashMap::is_empty")] - #[serde(default)] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "HashMap::is_empty"))] + #[cfg_attr(feature = "serde", serde(default))] pub options: HashMap, /// The path that this configuration was loaded from. /// /// 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)] pub path: Option, } +#[cfg(feature = "serde")] fn is_false(v: &bool) -> bool { !v } @@ -230,7 +233,7 @@ impl Config { res.map(|config| config.with_path(path)) } - #[cfg(feature = "json")] + #[cfg(feature = "json_config")] fn load_json>(path: P, data: &str) -> Result { serde_json::from_str(data).map_err(|e| InvalidConfig { 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>(path: P, _: &str) -> Result { Err(InvalidConfig { 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>(path: P, data: &str) -> Result { toml::from_str(data).map_err(|e| InvalidConfig { 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>(path: P, _: &str) -> Result { Err(InvalidConfig { 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>(path: P, data: &str) -> Result { serde_yaml::from_str(data).map_err(|e| InvalidConfig { 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>(path: P, _: &str) -> Result { Err(InvalidConfig { path: path.as_ref().to_string_lossy().into_owned(), @@ -308,7 +311,7 @@ impl Config { Ok(()) } - #[cfg(feature = "json")] + #[cfg(feature = "json_config")] fn save_json>(&self, path: &P) -> Result { serde_json::to_string(self).map_err(|e| InvalidConfig { 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>(&self, path: &P) -> Result { Err(InvalidConfig { 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>(&self, path: &P) -> Result { toml::to_string(self).map_err(|e| InvalidConfig { 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>(&self, path: &P) -> Result { Err(InvalidConfig { 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>(&self, path: &P) -> Result { serde_yaml::to_string(self).map_err(|e| InvalidConfig { 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>(&self, path: &P) -> Result { Err(InvalidConfig { path: path.as_ref().to_string_lossy().into_owned(), @@ -561,8 +564,7 @@ impl Config { #[cfg(test)] mod test { - use super::Config; - use anyhow::Result; + use super::{Config, Result}; use std::collections::HashMap; #[allow(unused)] @@ -609,7 +611,7 @@ mod test { } #[test] - #[cfg(feature = "json")] + #[cfg(feature = "json_config")] fn load_from_json() -> Result<()> { const DATA: &str = include_str!("client_config.json"); assert_eq!( @@ -620,7 +622,7 @@ mod test { } #[test] - #[cfg(feature = "toml")] + #[cfg(feature = "toml_config")] fn load_from_toml() -> Result<()> { const DATA: &str = include_str!("client_config.toml"); assert_eq!( @@ -631,7 +633,7 @@ mod test { } #[test] - #[cfg(feature = "yaml")] + #[cfg(feature = "yaml_config")] fn load_from_yaml() -> Result<()> { const DATA: &str = include_str!("client_config.yaml"); assert_eq!( diff --git a/src/error.rs b/src/error.rs index bafd1cf..ff78b49 100644 --- a/src/error.rs +++ b/src/error.rs @@ -94,17 +94,17 @@ pub enum Error { #[derive(Debug, Error)] pub enum ConfigError { /// Failed to parse as TOML. - #[cfg(feature = "toml")] + #[cfg(feature = "toml_config")] #[error("invalid toml")] InvalidToml(#[source] TomlError), /// Failed to parse as JSON. - #[cfg(feature = "json")] + #[cfg(feature = "json_config")] #[error("invalid json")] InvalidJson(#[source] serde_json::Error), /// Failed to parse as YAML. - #[cfg(feature = "yaml")] + #[cfg(feature = "yaml_config")] #[error("invalid yaml")] InvalidYaml(#[source] serde_yaml::Error), @@ -136,7 +136,7 @@ pub enum ConfigError { } /// A wrapper that combines toml's serialization and deserialization errors. -#[cfg(feature = "toml")] +#[cfg(feature = "toml_config")] #[derive(Debug, Error)] pub enum TomlError { /// A TOML deserialization error.