Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ParseInto trait for parsing config fields into rust types #454

Merged
merged 1 commit into from
Apr 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions components/core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
use std;
use std::fs::File;
use std::io::Read;
use std::net;
use std::path::Path;
use std::result;
use std::str::FromStr;

use toml;

use error::Error;
use error::{Error, Result};

pub trait ConfigFile: Sized {
type Error: std::error::Error + From<Error>;

fn from_file<T: AsRef<Path>>(filepath: T) -> Result<Self, Self::Error> {
fn from_file<T: AsRef<Path>>(filepath: T) -> result::Result<Self, Self::Error> {
let mut file = match File::open(filepath.as_ref()) {
Ok(f) => f,
Err(e) => return Err(Self::Error::from(Error::ConfigFileIO(e))),
Expand All @@ -36,7 +39,66 @@ pub trait ConfigFile: Sized {
}
}

fn from_toml(toml: toml::Table) -> Result<Self, Self::Error>;
fn from_toml(toml: toml::Table) -> result::Result<Self, Self::Error>;
}

pub trait ParseInto<T> {
fn parse_into(&self, field: &'static str, out: &mut T) -> Result<bool>;
}

impl ParseInto<net::SocketAddrV4> for toml::Table {
fn parse_into(&self, field: &'static str, out: &mut net::SocketAddrV4) -> Result<bool> {
if let Some(val) = self.get(field) {
if let Some(v) = val.as_str() {
match net::SocketAddrV4::from_str(v) {
Ok(addr) => {
*out = addr;
Ok(true)
}
Err(_) => Err(Error::ConfigInvalidSocketAddrV4(field, val.clone())),
}
} else {
Err(Error::ConfigInvalidSocketAddrV4(field, val.clone()))
}
} else {
Ok(false)
}
}
}

impl ParseInto<net::Ipv4Addr> for toml::Table {
fn parse_into(&self, field: &'static str, out: &mut net::Ipv4Addr) -> Result<bool> {
if let Some(val) = self.get(field) {
if let Some(v) = val.as_str() {
match net::Ipv4Addr::from_str(v) {
Ok(addr) => {
*out = addr;
Ok(true)
}
Err(_) => Err(Error::ConfigInvalidIpv4Addr(field, val.clone())),
}
} else {
Err(Error::ConfigInvalidIpv4Addr(field, val.clone()))
}
} else {
Ok(false)
}
}
}

impl ParseInto<String> for toml::Table {
fn parse_into(&self, field: &'static str, out: &mut String) -> Result<bool> {
if let Some(val) = self.get(field) {
if let Some(v) = val.as_str() {
*out = v.to_string();
Ok(true)
} else {
Err(Error::ConfigInvalidString(field, val.clone()))
}
} else {
Ok(false)
}
}
}

fn format_errors(parser: &toml::Parser) -> String {
Expand Down
23 changes: 23 additions & 0 deletions components/core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use std::string;

use libarchive;
use regex;
use toml;

use package;

Expand All @@ -30,6 +31,12 @@ pub enum Error {
ConfigFileIO(io::Error),
/// Parsing error while reading a configuratino file.
ConfigFileSyntax(String),
/// Expected a valid Ipv4 network address for configuration field value.
ConfigInvalidIpv4Addr(&'static str, toml::Value),
/// Expected a valid SocketAddrV4 address pair for configuration field value.
ConfigInvalidSocketAddrV4(&'static str, toml::Value),
/// Expected a string for configuration field value.
ConfigInvalidString(&'static str, toml::Value),
/// Crypto library error
CryptoError(String),
/// Occurs when a file that should exist does not or could not be read.
Expand Down Expand Up @@ -71,6 +78,19 @@ impl fmt::Display for Error {
format!("Syntax errors while parsing TOML configuration file:\n\n{}",
e)
}
Error::ConfigInvalidIpv4Addr(ref f, ref v) => {
format!("Invalid Ipv4 address in config, field={}, value={}. (example: \"127.0.0.0\")",
f,
v)
}
Error::ConfigInvalidSocketAddrV4(ref f, ref v) => {
format!("Invalid Ipv4 network address pair in config, field={}, value={}. (example: \"127.0.0.0:8080\")",
f,
v)
}
Error::ConfigInvalidString(ref f, ref v) => {
format!("Invalid string value in config, field={}, value={}", f, v)
}
Error::CryptoError(ref e) => format!("Crypto error: {}", e),
Error::FileNotFound(ref e) => format!("File not found at: {}", e),
Error::InvalidPackageIdent(ref e) => {
Expand Down Expand Up @@ -112,6 +132,9 @@ impl error::Error for Error {
Error::BadKeyPath(_) => "An absolute path to a file on disk is required",
Error::ConfigFileIO(_) => "Unable to read the raw contents of a configuration file",
Error::ConfigFileSyntax(_) => "Error parsing contents of configuration file",
Error::ConfigInvalidIpv4Addr(_, _) => "Invalid Ipv4 network address encountered while parsing a configuration file",
Error::ConfigInvalidSocketAddrV4(_, _) => "Invalid Ipv4 network address pair encountered while parsing a configuration file",
Error::ConfigInvalidString(_, _) => "Invalid string value encountered while parsing a configuration file",
Error::CryptoError(_) => "Crypto error",
Error::FileNotFound(_) => "File not found",
Error::InvalidPackageIdent(_) => "Package identifiers must be in origin/name format (example: acme/redis)",
Expand Down
57 changes: 6 additions & 51 deletions components/depot/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,28 @@
// open source license such as the Apache 2.0 License.

use std::net;
use std::str::FromStr;

use hcore::config::ConfigFile;
use hcore::config::{ConfigFile, ParseInto};
use redis;
use toml;

use super::{ListenAddr, ListenPort};
use error::{Error, Result};

#[derive(Debug, PartialEq, Eq)]
pub struct Config {
pub path: String,
pub listen_addr: ListenAddr,
pub port: ListenPort,
pub listen_addr: net::SocketAddrV4,
pub datastore_addr: net::SocketAddrV4,
}

impl Config {
pub fn depot_addr(&self) -> net::SocketAddrV4 {
net::SocketAddrV4::new(self.listen_addr.0.clone(), self.port.0.clone())
}
}

impl ConfigFile for Config {
type Error = Error;

fn from_toml(toml: toml::Table) -> Result<Self> {
let mut cfg = Config::default();
if let Some(value) = toml.get("path") {
match value {
&toml::Value::String(ref path) => cfg.path = path.clone(),
_ => panic!("JW TODO: handle this error"),
}
}
if let Some(value) = toml.get("bind_addr") {
match value {
&toml::Value::String(ref addr_str) => {
// JW TODO: handle this
let bind_addr = net::Ipv4Addr::from_str(addr_str).unwrap();
cfg.listen_addr = ListenAddr(bind_addr)
}
_ => panic!("JW TODO: handle this error"),
}
}
if let Some(value) = toml.get("port") {
match value {
&toml::Value::Integer(port) => cfg.port = ListenPort(port as u16),
_ => panic!("JW TODO: handle this error"),
}
}
if toml.contains_key("datastore_addr") || toml.contains_key("datastore_port") {
let ip = match toml.get("datastore_addr") {
Some(&toml::Value::String(ref addr_str)) => {
// JW TODO: handle this error
net::Ipv4Addr::from_str(addr_str).unwrap()
}
Some(_) => panic!("JW TODO: handle this error"),
None => net::Ipv4Addr::new(127, 0, 0, 1),
};
let port = match toml.get("datastore_port") {
Some(&toml::Value::Integer(port)) => port as u16,
Some(_) => panic!("handle"),
None => 6379,
};
cfg.datastore_addr = net::SocketAddrV4::new(ip, port);
}
try!(toml.parse_into("path", &mut cfg.path));
try!(toml.parse_into("bind_addr", &mut cfg.listen_addr));
try!(toml.parse_into("datastore_addr", &mut cfg.datastore_addr));
Ok(cfg)
}
}
Expand All @@ -80,8 +36,7 @@ impl Default for Config {
fn default() -> Self {
Config {
path: "/hab/svc/hab-depot/data".to_string(),
port: super::ListenPort::default(),
listen_addr: super::ListenAddr::default(),
listen_addr: net::SocketAddrV4::new(net::Ipv4Addr::new(0, 0, 0, 0), 9632),
datastore_addr: net::SocketAddrV4::new(net::Ipv4Addr::new(127, 0, 0, 1), 6379),
}
}
Expand Down
18 changes: 0 additions & 18 deletions components/depot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ pub mod server;
pub use self::config::Config;
pub use self::error::{Error, Result};

use std::net;
use std::sync::Arc;
use std::fs;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -104,20 +103,3 @@ impl Depot {
Path::new(&self.config.path).join("pkgs")
}
}

#[derive(Debug, PartialEq, Eq)]
pub struct ListenAddr(pub net::Ipv4Addr);
#[derive(Debug, PartialEq, Eq)]
pub struct ListenPort(pub u16);

impl Default for ListenAddr {
fn default() -> Self {
ListenAddr(net::Ipv4Addr::new(0, 0, 0, 0))
}
}

impl Default for ListenPort {
fn default() -> Self {
ListenPort(9632)
}
}
6 changes: 4 additions & 2 deletions components/depot/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extern crate env_logger;
#[macro_use]
extern crate log;

use std::net;
use std::process;
use std::str::FromStr;

Expand Down Expand Up @@ -74,7 +75,8 @@ fn config_from_args(matches: &clap::ArgMatches) -> Result<Config> {
};
if let Some(port) = args.value_of("port") {
if let Some(port) = u16::from_str(port).ok() {
config.port = depot::ListenPort(port);
let addr = net::SocketAddrV4::new(*config.listen_addr.ip(), port);
config.listen_addr = addr;
} else {
return Err(Error::BadPort(port.to_string()));
}
Expand Down Expand Up @@ -120,7 +122,7 @@ fn dispatch(config: Config, matches: &clap::ArgMatches) -> Result<()> {
/// * Fails if the depot server fails to start - canot bind to the port, etc.
fn start(config: Config) -> Result<()> {
println!("Starting package Depot at {}", &config.path);
println!("Depot listening on {:?}", config.depot_addr());
println!("Depot listening on {}", &config.listen_addr);
server::run(config)
}

Expand Down
2 changes: 1 addition & 1 deletion components/depot/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ impl AfterMiddleware for Cors {
}

pub fn run(config: Config) -> Result<()> {
let listen_addr = config.depot_addr();
let listen_addr = config.listen_addr.clone();
let depot = try!(Depot::new(config));
let depot1 = depot.clone();
let depot2 = depot.clone();
Expand Down