improve api

master
Ondřej Hruška 5 years ago
parent 59274c5c2a
commit 98dc572157
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 121
      src/lib.rs

@ -2,30 +2,23 @@
extern crate log; extern crate log;
use failure::{bail, Fallible}; use failure::{bail, Fallible};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::str::FromStr;
use serde::{Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use serde::de::DeserializeOwned; use std::path::{Path, PathBuf};
use std::str::FromStr;
pub const LOG_LEVELS: [&str; 5] = ["error", "warn", "info", "debug", "trace"]; pub const LOG_LEVELS: [&str; 5] = ["error", "warn", "info", "debug", "trace"];
/// 3rd-party libraries that produce log spam - we set these to a fixed higher level /// 3rd-party libraries that produce log spam - we set these to a fixed higher level
/// to allow using e.g. TRACE without drowning our custom messages /// to allow using e.g. TRACE without drowning our custom messages
pub const SPAMMY_LIBS: [&str; 6] = [ pub const SPAMMY_LIBS: [&str; 6] = ["tokio_reactor", "hyper", "reqwest", "mio", "want", "rumqtt"];
"tokio_reactor",
"hyper",
"reqwest",
"mio",
"want",
"rumqtt",
];
/// Implement this for the main config struct /// Implement this for the main config struct
pub trait BoilerplateCfg { pub trait AppConfig: Sized + Serialize + DeserializeOwned + Debug + Default {
type Init; type Init;
/// Get log level /// Get log level
@ -50,67 +43,64 @@ pub trait BoilerplateCfg {
/// Configure the config object using args. /// Configure the config object using args.
/// Logging has already been inited and configured. /// Logging has already been inited and configured.
fn configure<'a>(self, _clap: &clap::ArgMatches<'a>) -> Fallible<Self::Init>; fn configure<'a>(self, _clap: &clap::ArgMatches<'a>) -> Fallible<Self::Init>;
}
pub trait Boilerplate : BoilerplateCfg + Sized {
fn init(name: &str, cfg_file_name: &str, version : Option<String>) -> Fallible<Self::Init>;
}
fn read_file<P: AsRef<Path>>(path: P) -> Fallible<String> {
let path = path.as_ref();
let mut file = File::open(path)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
Ok(buf)
}
impl<CFG : BoilerplateCfg + Serialize + DeserializeOwned + Debug + Default> Boilerplate for CFG {
/// Initialize the app /// Initialize the app
fn init(name: &str, cfg_file_name: &str, version : Option<String>) -> Fallible<CFG::Init> fn init(name: &str, cfg_file_name: &str, version: Option<String>) -> Fallible<Self::Init> {
{ let version: String = version.unwrap_or_else(|| env!("CARGO_PKG_VERSION").into());
let version : String = version.unwrap_or_else(|| env!("CARGO_PKG_VERSION").into()); let clap = clap::App::new(name)
let clap = .arg(
clap::App::new(name) clap::Arg::with_name("config")
.arg(clap::Arg::with_name("config") .short("c")
.short("c").long("config") .long("config")
.value_name("FILE") .value_name("FILE")
.help("Sets a custom config file (JSON5)") .help("Sets a custom config file (JSON5)")
.takes_value(true)) .takes_value(true),
)
.arg(clap::Arg::with_name("dump-config") .arg(
clap::Arg::with_name("dump-config")
.long("dump-config") .long("dump-config")
.takes_value(false) .takes_value(false)
.help("Print the loaded config struct")) .help("Print the loaded config struct"),
)
.arg(clap::Arg::with_name("default-config") .arg(
clap::Arg::with_name("default-config")
.long("default-config") .long("default-config")
.takes_value(false) .takes_value(false)
.help("Print the default config JSON for reference (or to be piped to a file)")) .help("Print the default config JSON for reference (or to be piped to a file)"),
)
.arg(clap::Arg::with_name("verbose") .arg(
.short("v").long("verbose").multiple(true) clap::Arg::with_name("verbose")
.help("Increase logging verbosity (repeat to increase)")) .short("v")
.long("verbose")
.arg(clap::Arg::with_name("log-level") .multiple(true)
.help("Increase logging verbosity (repeat to increase)"),
)
.arg(
clap::Arg::with_name("log-level")
.long("log") .long("log")
.takes_value(true).value_name("LEVEL") .takes_value(true)
.value_name("LEVEL")
.validator(|s| { .validator(|s| {
if LOG_LEVELS.contains(&s.as_str()) { return Ok(()); } if LOG_LEVELS.contains(&s.as_str()) {
return Ok(());
}
Err(format!("Bad log level: {}", s)) Err(format!("Bad log level: {}", s))
}) })
.help("Set logging verbosity (error,warning,info,debug,trace)")); .help("Set logging verbosity (error,warning,info,debug,trace)"),
);
let clap = Self::add_args(clap); let clap = Self::add_args(clap);
// this must be done after `add_args` or all hell breaks loose around lifetimes // this must be done after `add_args` or all hell breaks loose around lifetimes
let clap = clap let clap = clap.version(version.as_str());
.version(version.as_str());
let argv = clap.get_matches(); let argv = clap.get_matches();
if argv.is_present("default-config") { if argv.is_present("default-config") {
println!("{}", serde_json::to_string_pretty(&CFG::default()).unwrap()); println!(
"{}",
serde_json::to_string_pretty(&Self::default()).unwrap()
);
std::process::exit(0); std::process::exit(0);
} }
@ -121,15 +111,18 @@ impl<CFG : BoilerplateCfg + Serialize + DeserializeOwned + Debug + Default> Boil
let filepath = PathBuf::from_str(cfg_file)?; let filepath = PathBuf::from_str(cfg_file)?;
let config: CFG = if filepath.is_file() { let config: Self = if filepath.is_file() {
println!("Load config from \"{}\"", cfg_file); println!("Load config from \"{}\"", cfg_file);
let buf = read_file(filepath)?; let buf = read_file(filepath)?;
json5::from_str(&buf)? json5::from_str(&buf)?
} else { } else {
println!("Config file \"{}\" does not exist, using defaults.", cfg_file); println!(
"Config file \"{}\" does not exist, using defaults.",
cfg_file
);
CFG::default() Self::default()
}; };
let mut level = config.logging().as_str(); let mut level = config.logging().as_str();
@ -156,10 +149,7 @@ impl<CFG : BoilerplateCfg + Serialize + DeserializeOwned + Debug + Default> Boil
/* Verbosity increased */ /* Verbosity increased */
if argv.is_present("verbose") { if argv.is_present("verbose") {
// bump verbosity if -v's are present // bump verbosity if -v's are present
let pos = LOG_LEVELS let pos = LOG_LEVELS.iter().position(|x| x == &level).unwrap();
.iter()
.position(|x| x == &level)
.unwrap();
level = match LOG_LEVELS level = match LOG_LEVELS
.iter() .iter()
@ -208,3 +198,12 @@ impl<CFG : BoilerplateCfg + Serialize + DeserializeOwned + Debug + Default> Boil
config.configure(&argv) config.configure(&argv)
} }
} }
fn read_file<P: AsRef<Path>>(path: P) -> Fallible<String> {
let path = path.as_ref();
let mut file = File::open(path)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
Ok(buf)
}

Loading…
Cancel
Save