fixes
This commit is contained in:
@@ -35,7 +35,7 @@ See the examples directory.
|
|||||||
The example called "rotn" implements rot13 as a command-line tool.
|
The example called "rotn" implements rot13 as a command-line tool.
|
||||||
|
|
||||||
```none
|
```none
|
||||||
$ cargo run --example complete -- -h
|
$ cargo run --example rotn -- -h
|
||||||
|
|
||||||
Rot-N 0.1.0 by Ondřej Hruška <ondra@ondrovo.com>
|
Rot-N 0.1.0 by Ondřej Hruška <ondra@ondrovo.com>
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -1,10 +1,11 @@
|
|||||||
|
//! This is a minimal example with a config file and logging
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clappconfig::AppConfig; // Must be in scope so the trait methods may be used
|
||||||
use clappconfig::AppConfig;
|
|
||||||
use failure::Fallible;
|
use failure::Fallible;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
@@ -15,19 +16,22 @@ struct Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppConfig for Config {
|
impl AppConfig for Config {
|
||||||
|
// We want init() to just return the config struct
|
||||||
type Init = Config;
|
type Init = Config;
|
||||||
|
|
||||||
|
/// Logging - we do not have a config option for this, so just return a default value
|
||||||
fn logging(&self) -> &str {
|
fn logging(&self) -> &str {
|
||||||
"info"
|
"info"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configure<'a>(self, _clap: &ArgMatches<'a>) -> Fallible<Config> {
|
/// Finalize config (this is called inside `init()`)
|
||||||
|
fn configure<'a>(self, _clap: &clap::ArgMatches<'a>) -> Fallible<Self::Init> {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Fallible<()> {
|
fn main() -> Fallible<()> {
|
||||||
let cfg = Config::init("Foo", "examples/foo.json", None)?;
|
let cfg = Config::init("Foo Example", "examples/foo.json", None)?;
|
||||||
|
|
||||||
trace!("Trace message");
|
trace!("Trace message");
|
||||||
debug!("Debug message");
|
debug!("Debug message");
|
||||||
@@ -35,7 +39,7 @@ fn main() -> Fallible<()> {
|
|||||||
warn!("Warn message");
|
warn!("Warn message");
|
||||||
error!("Error message");
|
error!("Error message");
|
||||||
|
|
||||||
println!("Welcome to foo, config:\n{:#?}", cfg);
|
println!("Welcome to foo, config is:\n{:#?}", cfg);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-23
@@ -1,3 +1,5 @@
|
|||||||
|
//! This is a complete example, showing how to use the boilerplate in a real-world application
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@@ -27,6 +29,8 @@ struct Config {
|
|||||||
offset: i32,
|
offset: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is a struct created by the `Config::configure()` method
|
||||||
|
/// (and returned by the `Config::init()`)
|
||||||
struct Main {
|
struct Main {
|
||||||
config: Config,
|
config: Config,
|
||||||
input_file: PathBuf,
|
input_file: PathBuf,
|
||||||
@@ -35,12 +39,15 @@ struct Main {
|
|||||||
impl AppConfig for Config {
|
impl AppConfig for Config {
|
||||||
type Init = Main;
|
type Init = Main;
|
||||||
|
|
||||||
|
/// Logging from the config struct
|
||||||
fn logging(&self) -> &str {
|
fn logging(&self) -> &str {
|
||||||
&self.logging
|
&self.logging
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exclude some spammy libs from logs
|
||||||
fn logging_suppress_mods(&self) -> Option<Vec<&str>> {
|
fn logging_suppress_mods(&self) -> Option<Vec<&str>> {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
|
// we do not actually use these here, this is an example
|
||||||
"tokio_reactor",
|
"tokio_reactor",
|
||||||
"tokio_io",
|
"tokio_io",
|
||||||
"hyper",
|
"hyper",
|
||||||
@@ -50,23 +57,24 @@ impl AppConfig for Config {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print startup banner.
|
/// We do not want a banner
|
||||||
/// May be overridden to customize or disable it.
|
|
||||||
fn print_banner(_name: &str, _version: &str) {
|
fn print_banner(_name: &str, _version: &str) {
|
||||||
// Disable
|
// Disable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Log messages printed before logging is set up
|
/// Suppress stderr logging on startup
|
||||||
/// May be overridden to customize or disable it.
|
|
||||||
fn pre_log_println(_message: String) {
|
fn pre_log_println(_message: String) {
|
||||||
// Disable
|
// Disable
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get log module levels to use (take priority over the main log level)
|
/// Get log module levels to use
|
||||||
|
/// (take priority over the main log level & suppressing)
|
||||||
fn logging_mod_levels(&self) -> Option<&HashMap<String, String>> {
|
fn logging_mod_levels(&self) -> Option<&HashMap<String, String>> {
|
||||||
|
// we let user configure these
|
||||||
Some(&self.log_modules)
|
Some(&self.log_modules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add our custom args
|
||||||
fn add_args<'a: 'b, 'b>(clap: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
|
fn add_args<'a: 'b, 'b>(clap: clap::App<'a, 'b>) -> clap::App<'a, 'b> {
|
||||||
// Default impl
|
// Default impl
|
||||||
clap.arg(
|
clap.arg(
|
||||||
@@ -74,8 +82,9 @@ impl AppConfig for Config {
|
|||||||
.short("i")
|
.short("i")
|
||||||
.long("input")
|
.long("input")
|
||||||
.value_name("FILE")
|
.value_name("FILE")
|
||||||
|
// Work-around for default-config acting as short-circuit (similar to --version and --help)
|
||||||
.required_unless("default-config")
|
.required_unless("default-config")
|
||||||
.help("Input file")
|
.help("Input file to rotate")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@@ -83,20 +92,30 @@ impl AppConfig for Config {
|
|||||||
.short("n")
|
.short("n")
|
||||||
.long("shift")
|
.long("shift")
|
||||||
.value_name("N")
|
.value_name("N")
|
||||||
.help("Positive or negative shift, default 13")
|
.help("Positive or negative rotation, default 13")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Here finalize configuration and parse our custom arguments.
|
||||||
|
///
|
||||||
|
/// Sometimes these go into `Config`, then we can use `type Init = Config;`
|
||||||
|
///
|
||||||
|
/// Here we don't want to have the input path in config, so a different struct is used.
|
||||||
|
///
|
||||||
|
/// This can also be solved using `#[serde(skip)]` on the field, but sometimes that is not
|
||||||
|
/// possible.
|
||||||
fn configure<'a>(mut self, clap: &ArgMatches<'a>) -> Fallible<Self::Init> {
|
fn configure<'a>(mut self, clap: &ArgMatches<'a>) -> Fallible<Self::Init> {
|
||||||
let input = clap.value_of("input").unwrap().to_string();
|
let input = clap.value_of("input").unwrap().to_string();
|
||||||
|
|
||||||
|
// Logging is initialized now - feel free to use it
|
||||||
debug!("Input: {}", input);
|
debug!("Input: {}", input);
|
||||||
|
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
bail!("Input is required!");
|
bail!("Input is required!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `?` can be used to abort
|
||||||
let pb = PathBuf::from_str(&input)?.canonicalize()?;
|
let pb = PathBuf::from_str(&input)?.canonicalize()?;
|
||||||
|
|
||||||
debug!("Input path: {}", pb.display());
|
debug!("Input path: {}", pb.display());
|
||||||
@@ -113,9 +132,11 @@ impl AppConfig for Config {
|
|||||||
debug!("Rot raw: {}", self.offset);
|
debug!("Rot raw: {}", self.offset);
|
||||||
|
|
||||||
self.offset = i32::rem_euclid(self.offset, 26);
|
self.offset = i32::rem_euclid(self.offset, 26);
|
||||||
|
assert!(self.offset < 26);
|
||||||
|
|
||||||
debug!("Rot normalized: {}", self.offset);
|
debug!("Rot normalized: {}", self.offset);
|
||||||
|
|
||||||
|
// We construct the `Init` struct here
|
||||||
Ok(Main {
|
Ok(Main {
|
||||||
config: self,
|
config: self,
|
||||||
input_file: pb,
|
input_file: pb,
|
||||||
@@ -123,21 +144,6 @@ impl AppConfig for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rot_n(string: String, rot: u8) -> String {
|
|
||||||
assert!(rot < 26);
|
|
||||||
const LOWER: &[u8] = b"abcdefghihjlmnopqrstuvwxyz";
|
|
||||||
const UPPER: &[u8] = b"ABCDEFGHIHJLMNOPQRSTUVWXYZ";
|
|
||||||
|
|
||||||
string
|
|
||||||
.chars()
|
|
||||||
.map(|c| match c {
|
|
||||||
'A'..='Z' => UPPER[((c as u8 - 'A' as u8 + rot) % 26) as usize] as char,
|
|
||||||
'a'..='z' => LOWER[((c as u8 - 'a' as u8 + rot) % 26) as usize] as char,
|
|
||||||
_ => c,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Fallible<()> {
|
fn main() -> Fallible<()> {
|
||||||
// Using custom version (default is CARGO_PKG_VERSION)
|
// Using custom version (default is CARGO_PKG_VERSION)
|
||||||
let version = format!(
|
let version = format!(
|
||||||
@@ -148,7 +154,11 @@ fn main() -> Fallible<()> {
|
|||||||
|
|
||||||
let main = Config::init("Rot-N", "rotn_config.json", Some(&version))?;
|
let main = Config::init("Rot-N", "rotn_config.json", Some(&version))?;
|
||||||
|
|
||||||
let mut f = OpenOptions::new().read(true).open(main.input_file)?;
|
// rot13
|
||||||
|
|
||||||
|
let mut f = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(main.input_file)?;
|
||||||
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
f.read_to_string(&mut buf)?;
|
f.read_to_string(&mut buf)?;
|
||||||
@@ -157,3 +167,20 @@ fn main() -> Fallible<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rotate a string
|
||||||
|
fn rot_n(string: String, rot: u8) -> String {
|
||||||
|
assert!(rot < 26);
|
||||||
|
|
||||||
|
const LOWER: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
|
||||||
|
const UPPER: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
|
||||||
|
string
|
||||||
|
.chars()
|
||||||
|
.map(|c| match c {
|
||||||
|
'A'..='Z' => UPPER[((c as u8 - 'A' as u8 + rot) % 26) as usize] as char,
|
||||||
|
'a'..='z' => LOWER[((c as u8 - 'a' as u8 + rot) % 26) as usize] as char,
|
||||||
|
_ => c,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|||||||
+2
-5
@@ -23,13 +23,13 @@ pub trait AppConfig: Sized + Serialize + DeserializeOwned + Debug + Default {
|
|||||||
/// Print startup banner.
|
/// Print startup banner.
|
||||||
/// May be overridden to customize or disable it.
|
/// May be overridden to customize or disable it.
|
||||||
fn print_banner(name: &str, version: &str) {
|
fn print_banner(name: &str, version: &str) {
|
||||||
println!("{} {}\nRun with -h for help", name, version);
|
eprintln!("{} {}\nRun with -h for help", name, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Log messages printed before logging is set up
|
/// Log messages printed before logging is set up
|
||||||
/// May be overridden to customize or disable it.
|
/// May be overridden to customize or disable it.
|
||||||
fn pre_log_println(message: String) {
|
fn pre_log_println(message: String) {
|
||||||
println!("{}", message);
|
eprintln!("{}", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get names of library modules to suppress from log output (limit to warn or error)
|
/// Get names of library modules to suppress from log output (limit to warn or error)
|
||||||
@@ -172,9 +172,6 @@ pub trait AppConfig: Sized + Serialize + DeserializeOwned + Debug + Default {
|
|||||||
let env = env_logger::Env::default().default_filter_or(level);
|
let env = env_logger::Env::default().default_filter_or(level);
|
||||||
let mut builder = env_logger::Builder::from_env(env);
|
let mut builder = env_logger::Builder::from_env(env);
|
||||||
|
|
||||||
// add a newline
|
|
||||||
println!();
|
|
||||||
|
|
||||||
builder.format_timestamp_millis();
|
builder.format_timestamp_millis();
|
||||||
|
|
||||||
let mut per_mod = vec![];
|
let mut per_mod = vec![];
|
||||||
|
|||||||
Reference in New Issue
Block a user