rust-irc/src/client/data/config.rs

281 lines
10 KiB
Rust
Raw Normal View History

//! JSON configuration files using libserialize.
use std::borrow::ToOwned;
2014-11-02 18:16:49 -05:00
use std::collections::HashMap;
2015-03-02 17:12:16 -05:00
use std::fs::File;
use std::io::prelude::*;
use std::io::{Error, ErrorKind, Result};
2015-03-07 12:15:22 -05:00
use std::path::Path;
2015-09-16 12:21:58 -04:00
use rustc_serialize::json::{decode, encode};
2014-11-02 18:16:49 -05:00
/// Configuration data.
2015-09-13 13:49:15 +01:00
#[derive(Clone, RustcDecodable, RustcEncodable, Default, PartialEq, Debug)]
2014-11-02 18:16:49 -05:00
pub struct Config {
/// A list of the owners of the client by nickname (for bots).
pub owners: Option<Vec<String>>,
/// The client's nickname.
pub nickname: Option<String>,
/// The client's NICKSERV password.
pub nick_password: Option<String>,
/// Alternative nicknames for the client, if the default is taken.
pub alt_nicks: Option<Vec<String>>,
/// The client's username.
pub username: Option<String>,
/// The client's real name.
pub realname: Option<String>,
/// The server to connect to.
pub server: Option<String>,
/// The port to connect on.
pub port: Option<u16>,
/// The password to connect to the server.
pub password: Option<String>,
/// Whether or not to use SSL.
/// Clients will automatically panic if this is enabled without SSL support.
pub use_ssl: Option<bool>,
/// The encoding type used for this connection.
/// This is typically UTF-8, but could be something else.
pub encoding: Option<String>,
/// A list of channels to join on connection.
pub channels: Option<Vec<String>>,
/// User modes to set on connect. Example: "+RB-x"
pub umodes: Option<String>,
/// The text that'll be sent in response to CTCP USERINFO requests.
pub user_info: Option<String>,
/// The amount of inactivity in seconds before the client will ping the server.
pub ping_time: Option<u32>,
/// The amount of time in seconds for a client to reconnect due to no ping response.
pub ping_timeout: Option<u32>,
2016-02-09 18:24:52 +08:00
/// 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.
pub should_ghost: Option<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.
pub ghost_sequence: Option<Vec<String>>,
/// A map of additional options to be stored in config.
pub options: Option<HashMap<String, String>>,
2014-11-02 18:16:49 -05:00
}
impl Config {
/// Loads a JSON configuration from the desired path.
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config> {
2015-03-07 15:01:34 -05:00
let mut file = try!(File::open(path));
2015-03-02 17:12:16 -05:00
let mut data = String::new();
try!(file.read_to_string(&mut data));
2015-06-22 12:03:57 -04:00
decode(&data[..]).map_err(|_|
2015-04-03 00:56:42 -04:00
Error::new(ErrorKind::InvalidInput, "Failed to decode configuration file.")
2015-03-02 17:12:16 -05:00
)
2014-11-02 18:16:49 -05:00
}
2015-09-16 12:21:58 -04:00
/// Saves a JSON configuration to the desired path.
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
let mut file = try!(File::create(path));
file.write_all(try!(encode(self).map_err(|_|
Error::new(ErrorKind::InvalidInput, "Failed to encode configuration file.")
)).as_bytes())
}
/// Determines whether or not the nickname provided is the owner of the bot.
2014-11-02 18:16:49 -05:00
pub fn is_owner(&self, nickname: &str) -> bool {
2015-04-04 23:06:13 -04:00
self.owners.as_ref().map(|o| o.contains(&nickname.to_string())).unwrap()
}
/// Gets the nickname specified in the configuration.
/// This will panic if not specified.
pub fn nickname(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.nickname.as_ref().map(|s| &s[..]).unwrap()
}
/// Gets the bot's nickserv password specified in the configuration.
/// This defaults to an empty string when not specified.
pub fn nick_password(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.nick_password.as_ref().map(|s| &s[..]).unwrap_or("")
}
/// Gets the alternate nicknames specified in the configuration.
/// This defaults to an empty vector when not specified.
pub fn alternate_nicknames(&self) -> Vec<&str> {
2015-02-21 09:28:12 -05:00
self.alt_nicks.as_ref().map(|v| v.iter().map(|s| &s[..]).collect()).unwrap_or(vec![])
}
/// Gets the username specified in the configuration.
/// This defaults to the user's nickname when not specified.
pub fn username(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.username.as_ref().map(|s| &s[..]).unwrap_or(self.nickname())
}
/// Gets the real name specified in the configuration.
/// This defaults to the user's nickname when not specified.
pub fn real_name(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.realname.as_ref().map(|s| &s[..]).unwrap_or(self.nickname())
}
/// Gets the address of the server specified in the configuration.
/// This panics when not specified.
pub fn server(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.server.as_ref().map(|s| &s[..]).unwrap()
}
/// Gets the port of the server specified in the configuration.
/// This defaults to 6667 (or 6697 if use_ssl is specified as true) when not specified.
pub fn port(&self) -> u16 {
self.port.as_ref().map(|p| *p).unwrap_or(if self.use_ssl() {
6697
} else {
6667
})
}
/// Gets the server password specified in the configuration.
/// This defaults to a blank string when not specified.
pub fn password(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.password.as_ref().map(|s| &s[..]).unwrap_or("")
}
/// Gets whether or not to use SSL with this connection.
/// This defaults to false when not specified.
pub fn use_ssl(&self) -> bool {
self.use_ssl.as_ref().map(|u| *u).unwrap_or(false)
}
/// Gets the encoding to use for this connection. This requires the encode feature to work.
/// This defaults to UTF-8 when not specified.
pub fn encoding(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.encoding.as_ref().map(|s| &s[..]).unwrap_or("UTF-8")
}
/// Gets the channels to join upon connection.
/// This defaults to an empty vector if it's not specified.
pub fn channels(&self) -> Vec<&str> {
2015-06-22 12:03:57 -04:00
self.channels.as_ref().map(|v| v.iter().map(|s| &s[..]).collect()).unwrap_or(vec![])
2014-11-02 18:16:49 -05:00
}
/// Gets the user modes to set on connect specified in the configuration.
/// This defaults to an empty string when not specified.
pub fn umodes(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.umodes.as_ref().map(|s| &s[..]).unwrap_or("")
}
/// Gets the string to be sent in response to CTCP USERINFO requests.
/// This defaults to an empty string when not specified.
pub fn user_info(&self) -> &str {
2015-02-21 09:28:12 -05:00
self.user_info.as_ref().map(|s| &s[..]).unwrap_or("")
}
/// Gets the amount of time in seconds since last activity necessary for the client to ping the
/// server.
/// This defaults to 180 seconds when not specified.
pub fn ping_time(&self) -> u32 {
self.ping_time.as_ref().map(|t| *t).unwrap_or(180)
}
/// Gets the amount of time in seconds for the client to reconnect after no ping response.
/// This defaults to 10 seconds when not specified.
pub fn ping_timeout(&self) -> u32 {
self.ping_timeout.as_ref().map(|t| *t).unwrap_or(10)
}
2016-02-09 18:24:52 +08:00
/// Gets whether or not to use NickServ GHOST
/// This defaults to false when not specified.
pub fn should_ghost(&self) -> bool {
self.should_ghost.as_ref().map(|u| *u).unwrap_or(false)
}
/// Gets the NickServ command sequence to recover a nickname.
/// This defaults to `["GHOST"]` when not specified.
pub fn ghost_sequence(&self) -> Vec<&str> {
self.ghost_sequence.as_ref().map(|v| v.iter().map(|s| &s[..]).collect()).unwrap_or(vec!["GHOST"])
}
/// Looks up the specified string in the options map.
/// This uses indexing, and thus panics when the string is not present.
/// This will also panic if used and there are no options.
pub fn get_option(&self, option: &str) -> &str {
2015-03-26 21:50:38 -04:00
self.options.as_ref().map(|o| &o[&option.to_owned()][..]).unwrap()
}
2014-11-02 18:16:49 -05:00
}
#[cfg(test)]
mod test {
use super::Config;
use std::collections::HashMap;
use std::default::Default;
2015-03-07 15:01:34 -05:00
use std::path::Path;
#[test]
fn load() {
let cfg = Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
nick_password: None,
alt_nicks: None,
username: Some(format!("test")),
realname: Some(format!("test")),
password: Some(String::new()),
umodes: Some(format!("+BR")),
server: Some(format!("irc.test.net")),
port: Some(6667),
use_ssl: Some(false),
encoding: Some(format!("UTF-8")),
channels: Some(vec![format!("#test"), format!("#test2")]),
2014-12-23 12:15:41 -05:00
user_info: None,
2016-01-16 11:47:25 -05:00
ping_time: None,
ping_timeout: None,
2016-02-09 18:24:52 +08:00
should_ghost: None,
ghost_sequence: None,
options: Some(HashMap::new()),
};
2015-04-03 00:56:42 -04:00
assert_eq!(Config::load(Path::new("client_config.json")).unwrap(), cfg);
}
#[test]
fn load_from_str() {
let cfg = Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
nick_password: None,
alt_nicks: None,
username: Some(format!("test")),
realname: Some(format!("test")),
umodes: Some(format!("+BR")),
password: Some(String::new()),
server: Some(format!("irc.test.net")),
port: Some(6667),
use_ssl: Some(false),
encoding: Some(format!("UTF-8")),
channels: Some(vec![format!("#test"), format!("#test2")]),
2014-12-23 12:15:41 -05:00
user_info: None,
2016-01-16 11:47:25 -05:00
ping_time: None,
ping_timeout: None,
2016-02-09 18:24:52 +08:00
should_ghost: None,
ghost_sequence: None,
options: Some(HashMap::new()),
};
assert_eq!(Config::load("client_config.json").unwrap(), cfg);
}
#[test]
fn is_owner() {
let cfg = Config {
owners: Some(vec![format!("test"), format!("test2")]),
.. Default::default()
};
assert!(cfg.is_owner("test"));
assert!(cfg.is_owner("test2"));
assert!(!cfg.is_owner("test3"));
}
#[test]
fn get_option() {
let cfg = Config {
options: {
let mut map = HashMap::new();
map.insert(format!("testing"), format!("test"));
Some(map)
},
.. Default::default()
};
assert_eq!(cfg.get_option("testing"), "test");
}
}