Make serde optional
This commit is contained in:
parent
2339ca5fe6
commit
dd09555537
5 changed files with 81 additions and 74 deletions
|
@ -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
|
||||
|
|
15
Cargo.toml
15
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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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<String>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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<String>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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<String>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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<String>,
|
||||
/// 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<String>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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>,
|
||||
/// 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<u32>,
|
||||
/// 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<u32>,
|
||||
/// 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<Vec<String>>,
|
||||
/// 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<String>,
|
||||
|
||||
/// 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<String, String>,
|
||||
/// 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<String, String>,
|
||||
|
||||
/// 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<PathBuf>,
|
||||
}
|
||||
|
||||
#[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<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> {
|
||||
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<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> {
|
||||
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<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> {
|
||||
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<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> {
|
||||
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<P: AsRef<Path>>(path: P, data: &str) -> Result<Config> {
|
||||
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<P: AsRef<Path>>(path: P, _: &str) -> Result<Config> {
|
||||
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<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
|
||||
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<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
|
||||
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<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
|
||||
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<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
|
||||
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<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
|
||||
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<P: AsRef<Path>>(&self, path: &P) -> Result<String> {
|
||||
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!(
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue