Added support for toml and yaml configurations.
This commit is contained in:
parent
a63dbb5422
commit
c749146d5c
6 changed files with 274 additions and 77 deletions
|
@ -7,10 +7,10 @@ sudo: false
|
||||||
script:
|
script:
|
||||||
- chmod +x mktestconfig.sh
|
- chmod +x mktestconfig.sh
|
||||||
- ./mktestconfig.sh
|
- ./mktestconfig.sh
|
||||||
|
- cargo build --verbose --features "toml yaml"
|
||||||
|
- cargo test --verbose --features "toml yaml"
|
||||||
- cargo build --verbose --no-default-features
|
- cargo build --verbose --no-default-features
|
||||||
- cargo build --verbose
|
|
||||||
- cargo test --verbose --no-default-features
|
- cargo test --verbose --no-default-features
|
||||||
- cargo test --verbose
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
irc:
|
irc:
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -11,9 +11,11 @@ repository = "https://github.com/aatxe/irc"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["ctcp"]
|
default = ["ctcp", "json"]
|
||||||
ctcp = []
|
ctcp = []
|
||||||
nochanlists = []
|
nochanlists = []
|
||||||
|
json = ["serde_json"]
|
||||||
|
yaml = ["serde_yaml"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bufstream = "0.1"
|
bufstream = "0.1"
|
||||||
|
@ -25,9 +27,15 @@ futures = "0.1"
|
||||||
native-tls = "0.1"
|
native-tls = "0.1"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
serde_yaml = { version = "0.7", optional = true }
|
||||||
tokio-core = "0.1"
|
tokio-core = "0.1"
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
tokio-mockstream = "1.1"
|
tokio-mockstream = "1.1"
|
||||||
tokio-timer = "0.1"
|
tokio-timer = "0.1"
|
||||||
tokio-tls = "0.1"
|
tokio-tls = "0.1"
|
||||||
|
toml = { version = "0.4", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
args = "2.0"
|
||||||
|
getopts = "0.2"
|
||||||
|
|
62
examples/convertconf.rs
Normal file
62
examples/convertconf.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
extern crate args;
|
||||||
|
extern crate getopts;
|
||||||
|
extern crate irc;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use args::{Args, ArgsError};
|
||||||
|
use getopts::Occur;
|
||||||
|
use irc::client::data::Config;
|
||||||
|
|
||||||
|
const PROGRAM_DESC: &'static str = "Use this program to convert configs between {JSON, TOML, YAML}.";
|
||||||
|
const PROGRAM_NAME: &'static str = "convertconf";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<_> = env::args().collect();
|
||||||
|
match parse(&args) {
|
||||||
|
Ok(Some((ref input, ref output))) => {
|
||||||
|
let cfg = Config::load(input).unwrap();
|
||||||
|
cfg.save(output).unwrap();
|
||||||
|
println!("Converted {} to {}.", input, output);
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
println!("Failed to provide required arguments.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &Vec<String>) -> Result<Option<(String, String)>, ArgsError> {
|
||||||
|
let mut args = Args::new(PROGRAM_NAME, PROGRAM_DESC);
|
||||||
|
args.flag("h", "help", "Print the usage menu");
|
||||||
|
args.option("i",
|
||||||
|
"input",
|
||||||
|
"The path to the input config",
|
||||||
|
"FILE",
|
||||||
|
Occur::Req,
|
||||||
|
None);
|
||||||
|
args.option("o",
|
||||||
|
"output",
|
||||||
|
"The path to output the new config to",
|
||||||
|
"FILE",
|
||||||
|
Occur::Req,
|
||||||
|
None);
|
||||||
|
|
||||||
|
args.parse(input)?;
|
||||||
|
|
||||||
|
let help = args.value_of("help")?;
|
||||||
|
if help {
|
||||||
|
args.full_usage();
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some((
|
||||||
|
args.value_of("input")?,
|
||||||
|
args.value_of("output")?,
|
||||||
|
)))
|
||||||
|
}
|
|
@ -1 +1,3 @@
|
||||||
echo "{\"owners\": [\"test\"],\"nickname\": \"test\",\"username\": \"test\",\"realname\": \"test\",\"password\": \"\",\"server\": \"irc.test.net\",\"port\": 6667,\"use_ssl\": false,\"encoding\": \"UTF-8\",\"channels\": [\"#test\", \"#test2\"],\"umodes\": \"+BR\",\"options\": {}}" > client_config.json
|
echo "{\"owners\": [\"test\"],\"nickname\": \"test\",\"username\": \"test\",\"realname\": \"test\",\"password\": \"\",\"server\": \"irc.test.net\",\"port\": 6667,\"use_ssl\": false,\"encoding\": \"UTF-8\",\"channels\": [\"#test\", \"#test2\"],\"umodes\": \"+BR\",\"options\": {}}" > client_config.json
|
||||||
|
cargo run --example convertconf --features "toml yaml" -- -i client_config.json -o client_config.toml
|
||||||
|
cargo run --example convertconf --features "toml yaml" -- -i client_config.json -o client_config.yaml
|
||||||
|
|
|
@ -7,7 +7,12 @@ use std::io::{Error, ErrorKind};
|
||||||
use std::net::{SocketAddr, ToSocketAddrs};
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
#[cfg(feature = "yaml")]
|
||||||
|
use serde_yaml;
|
||||||
|
#[cfg(feature = "toml")]
|
||||||
|
use toml;
|
||||||
|
|
||||||
use error::Result;
|
use error::Result;
|
||||||
|
|
||||||
|
@ -80,30 +85,157 @@ pub struct Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Loads a JSON configuration from the desired path.
|
/// Loads a configuration from the desired path. This will use the file extension to detect
|
||||||
|
/// which format to parse the file as (json, toml, or yaml). Using each format requires having
|
||||||
|
/// its respective crate feature enabled. Only json is available by default.
|
||||||
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config> {
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Config> {
|
||||||
let mut file = File::open(path)?;
|
let mut file = File::open(&path)?;
|
||||||
let mut data = String::new();
|
let mut data = String::new();
|
||||||
file.read_to_string(&mut data)?;
|
file.read_to_string(&mut data)?;
|
||||||
|
|
||||||
|
match path.as_ref().extension().and_then(|s| s.to_str()) {
|
||||||
|
Some("json") => Config::load_json(&data),
|
||||||
|
Some("toml") => Config::load_toml(&data),
|
||||||
|
Some("yaml") | Some("yml") => Config::load_yaml(&data),
|
||||||
|
Some(ext) => return Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
format!("Failed to decode configuration of unknown format {}", ext),
|
||||||
|
).into()),
|
||||||
|
None => return Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Failed to decode configuration of missing or non-unicode format.",
|
||||||
|
).into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
fn load_json(data: &str) -> Result<Config> {
|
||||||
serde_json::from_str(&data[..]).map_err(|_| {
|
serde_json::from_str(&data[..]).map_err(|_| {
|
||||||
Error::new(
|
Error::new(
|
||||||
ErrorKind::InvalidInput,
|
ErrorKind::InvalidInput,
|
||||||
"Failed to decode configuration file.",
|
"Failed to decode JSON configuration file.",
|
||||||
).into()
|
).into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Saves a JSON configuration to the desired path.
|
#[cfg(not(feature = "json"))]
|
||||||
|
fn load_json(_: &str) -> Result<Config> {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"JSON file decoding is disabled.",
|
||||||
|
).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "toml")]
|
||||||
|
fn load_toml(data: &str) -> Result<Config> {
|
||||||
|
toml::from_str(&data[..]).map_err(|_| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Failed to decode TOML configuration file.",
|
||||||
|
).into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "toml"))]
|
||||||
|
fn load_toml(_: &str) -> Result<Config> {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"TOML file decoding is disabled.",
|
||||||
|
).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "yaml")]
|
||||||
|
fn load_yaml(data: &str) -> Result<Config> {
|
||||||
|
serde_yaml::from_str(&data[..]).map_err(|_| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Failed to decode YAML configuration file.",
|
||||||
|
).into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "yaml"))]
|
||||||
|
fn load_yaml(_: &str) -> Result<Config> {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"YAML file decoding is disabled.",
|
||||||
|
).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves a configuration to the desired path. This will use the file extension to detect
|
||||||
|
/// which format to parse the file as (json, toml, or yaml). Using each format requires having
|
||||||
|
/// its respective crate feature enabled. Only json is available by default.
|
||||||
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||||
let mut file = File::create(path)?;
|
let mut file = File::create(&path)?;
|
||||||
file.write_all(
|
let data = match path.as_ref().extension().and_then(|s| s.to_str()) {
|
||||||
serde_json::to_string(self).map_err(|_| {
|
Some("json") => self.save_json()?,
|
||||||
Error::new(
|
Some("toml") => self.save_toml()?,
|
||||||
ErrorKind::InvalidInput,
|
Some("yaml") | Some("yml") => self.save_yaml()?,
|
||||||
"Failed to encode configuration file.",
|
Some(ext) => return Err(Error::new(
|
||||||
)
|
ErrorKind::InvalidInput,
|
||||||
})?.as_bytes(),
|
format!("Failed to encode configuration of unknown format {}", ext),
|
||||||
).map_err(|e| e.into())
|
).into()),
|
||||||
|
None => return Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Failed to encode configuration of missing or non-unicode format.",
|
||||||
|
).into()),
|
||||||
|
};
|
||||||
|
file.write_all(data.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "json")]
|
||||||
|
fn save_json(&self) -> Result<String> {
|
||||||
|
serde_json::to_string(self).map_err(|_| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Failed to encode JSON configuration file.",
|
||||||
|
).into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "json"))]
|
||||||
|
fn save_json(&self) -> Result<String> {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"JSON file encoding is disabled.",
|
||||||
|
).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "toml")]
|
||||||
|
fn save_toml(&self) -> Result<String> {
|
||||||
|
toml::to_string(self).map_err(|_| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Failed to encode TOML configuration file.",
|
||||||
|
).into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "toml"))]
|
||||||
|
fn save_toml(&self) -> Result<String> {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"TOML file encoding is disabled.",
|
||||||
|
).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "yaml")]
|
||||||
|
fn save_yaml(&self) -> Result<String> {
|
||||||
|
serde_yaml::to_string(self).map_err(|_| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"Failed to encode YAML configuration file.",
|
||||||
|
).into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "yaml"))]
|
||||||
|
fn save_yaml(&self) -> Result<String> {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
"YAML file encoding is disabled.",
|
||||||
|
).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether or not the nickname provided is the owner of the bot.
|
/// Determines whether or not the nickname provided is the owner of the bot.
|
||||||
|
@ -308,78 +440,66 @@ impl Config {
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
|
#[cfg(feature = "json")]
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::Config;
|
use super::Config;
|
||||||
|
|
||||||
|
fn test_config() -> Config {
|
||||||
|
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),
|
||||||
|
cert_path: None,
|
||||||
|
encoding: Some(format!("UTF-8")),
|
||||||
|
channels: Some(vec![format!("#test"), format!("#test2")]),
|
||||||
|
channel_keys: None,
|
||||||
|
user_info: None,
|
||||||
|
version: None,
|
||||||
|
source: None,
|
||||||
|
ping_time: None,
|
||||||
|
ping_timeout: None,
|
||||||
|
burst_window_length: None,
|
||||||
|
max_messages_in_burst: None,
|
||||||
|
should_ghost: None,
|
||||||
|
ghost_sequence: None,
|
||||||
|
options: Some(HashMap::new()),
|
||||||
|
use_mock_connection: None,
|
||||||
|
mock_initial_value: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "json")]
|
||||||
fn load() {
|
fn load() {
|
||||||
let cfg = Config {
|
assert_eq!(Config::load(Path::new("client_config.json")).unwrap(), test_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),
|
|
||||||
cert_path: None,
|
|
||||||
encoding: Some(format!("UTF-8")),
|
|
||||||
channels: Some(vec![format!("#test"), format!("#test2")]),
|
|
||||||
channel_keys: None,
|
|
||||||
user_info: None,
|
|
||||||
version: None,
|
|
||||||
source: None,
|
|
||||||
ping_time: None,
|
|
||||||
ping_timeout: None,
|
|
||||||
burst_window_length: None,
|
|
||||||
max_messages_in_burst: None,
|
|
||||||
should_ghost: None,
|
|
||||||
ghost_sequence: None,
|
|
||||||
options: Some(HashMap::new()),
|
|
||||||
use_mock_connection: None,
|
|
||||||
mock_initial_value: None,
|
|
||||||
};
|
|
||||||
assert_eq!(Config::load(Path::new("client_config.json")).unwrap(), cfg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "json")]
|
||||||
fn load_from_str() {
|
fn load_from_str() {
|
||||||
let cfg = Config {
|
assert_eq!(Config::load("client_config.json").unwrap(), test_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),
|
|
||||||
cert_path: None,
|
|
||||||
encoding: Some(format!("UTF-8")),
|
|
||||||
channels: Some(vec![format!("#test"), format!("#test2")]),
|
|
||||||
channel_keys: None,
|
|
||||||
user_info: None,
|
|
||||||
version: None,
|
|
||||||
source: None,
|
|
||||||
ping_time: None,
|
|
||||||
ping_timeout: None,
|
|
||||||
burst_window_length: None,
|
|
||||||
max_messages_in_burst: None,
|
|
||||||
should_ghost: None,
|
|
||||||
ghost_sequence: None,
|
|
||||||
options: Some(HashMap::new()),
|
|
||||||
use_mock_connection: None,
|
|
||||||
mock_initial_value: None,
|
|
||||||
};
|
|
||||||
assert_eq!(Config::load("client_config.json").unwrap(), cfg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "toml")]
|
||||||
|
fn load_from_toml() {
|
||||||
|
assert_eq!(Config::load("client_config.toml").unwrap(), test_config());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "yaml")]
|
||||||
|
fn load_from_yaml() {
|
||||||
|
assert_eq!(Config::load("client_config.yaml").unwrap(), test_config());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_owner() {
|
fn is_owner() {
|
||||||
|
|
|
@ -15,12 +15,17 @@ extern crate native_tls;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
#[cfg(feature = "json")]
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
#[cfg(feature = "yaml")]
|
||||||
|
extern crate serde_yaml;
|
||||||
extern crate tokio_core;
|
extern crate tokio_core;
|
||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate tokio_mockstream;
|
extern crate tokio_mockstream;
|
||||||
extern crate tokio_timer;
|
extern crate tokio_timer;
|
||||||
extern crate tokio_tls;
|
extern crate tokio_tls;
|
||||||
|
#[cfg(feature = "toml")]
|
||||||
|
extern crate toml;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|
Loading…
Reference in a new issue