improve api
This commit is contained in:
+59
-60
@@ -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 =
|
let clap = clap::App::new(name)
|
||||||
clap::App::new(name)
|
.arg(
|
||||||
.arg(clap::Arg::with_name("config")
|
clap::Arg::with_name("config")
|
||||||
.short("c").long("config")
|
.short("c")
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user