|
|
|
@ -1,3 +1,5 @@ |
|
|
|
|
//! This is a complete example, showing how to use the boilerplate in a real-world application
|
|
|
|
|
|
|
|
|
|
#[macro_use] |
|
|
|
|
extern crate serde_derive; |
|
|
|
|
#[macro_use] |
|
|
|
@ -27,6 +29,8 @@ struct Config { |
|
|
|
|
offset: i32, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// This is a struct created by the `Config::configure()` method
|
|
|
|
|
/// (and returned by the `Config::init()`)
|
|
|
|
|
struct Main { |
|
|
|
|
config: Config, |
|
|
|
|
input_file: PathBuf, |
|
|
|
@ -35,12 +39,15 @@ struct Main { |
|
|
|
|
impl AppConfig for Config { |
|
|
|
|
type Init = Main; |
|
|
|
|
|
|
|
|
|
/// Logging from the config struct
|
|
|
|
|
fn logging(&self) -> &str { |
|
|
|
|
&self.logging |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Exclude some spammy libs from logs
|
|
|
|
|
fn logging_suppress_mods(&self) -> Option<Vec<&str>> { |
|
|
|
|
Some(vec![ |
|
|
|
|
// we do not actually use these here, this is an example
|
|
|
|
|
"tokio_reactor", |
|
|
|
|
"tokio_io", |
|
|
|
|
"hyper", |
|
|
|
@ -50,23 +57,24 @@ impl AppConfig for Config { |
|
|
|
|
]) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Print startup banner.
|
|
|
|
|
/// May be overridden to customize or disable it.
|
|
|
|
|
/// We do not want a banner
|
|
|
|
|
fn print_banner(_name: &str, _version: &str) { |
|
|
|
|
// Disable
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Log messages printed before logging is set up
|
|
|
|
|
/// May be overridden to customize or disable it.
|
|
|
|
|
/// Suppress stderr logging on startup
|
|
|
|
|
fn pre_log_println(_message: String) { |
|
|
|
|
// 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>> { |
|
|
|
|
// we let user configure these
|
|
|
|
|
Some(&self.log_modules) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Add our custom args
|
|
|
|
|
fn add_args<'a: 'b, 'b>(clap: clap::App<'a, 'b>) -> clap::App<'a, 'b> { |
|
|
|
|
// Default impl
|
|
|
|
|
clap.arg( |
|
|
|
@ -74,8 +82,9 @@ impl AppConfig for Config { |
|
|
|
|
.short("i") |
|
|
|
|
.long("input") |
|
|
|
|
.value_name("FILE") |
|
|
|
|
// Work-around for default-config acting as short-circuit (similar to --version and --help)
|
|
|
|
|
.required_unless("default-config") |
|
|
|
|
.help("Input file") |
|
|
|
|
.help("Input file to rotate") |
|
|
|
|
.takes_value(true), |
|
|
|
|
) |
|
|
|
|
.arg( |
|
|
|
@ -83,20 +92,30 @@ impl AppConfig for Config { |
|
|
|
|
.short("n") |
|
|
|
|
.long("shift") |
|
|
|
|
.value_name("N") |
|
|
|
|
.help("Positive or negative shift, default 13") |
|
|
|
|
.help("Positive or negative rotation, default 13") |
|
|
|
|
.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> { |
|
|
|
|
let input = clap.value_of("input").unwrap().to_string(); |
|
|
|
|
|
|
|
|
|
// Logging is initialized now - feel free to use it
|
|
|
|
|
debug!("Input: {}", input); |
|
|
|
|
|
|
|
|
|
if input.is_empty() { |
|
|
|
|
bail!("Input is required!"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// `?` can be used to abort
|
|
|
|
|
let pb = PathBuf::from_str(&input)?.canonicalize()?; |
|
|
|
|
|
|
|
|
|
debug!("Input path: {}", pb.display()); |
|
|
|
@ -113,9 +132,11 @@ impl AppConfig for Config { |
|
|
|
|
debug!("Rot raw: {}", self.offset); |
|
|
|
|
|
|
|
|
|
self.offset = i32::rem_euclid(self.offset, 26); |
|
|
|
|
assert!(self.offset < 26); |
|
|
|
|
|
|
|
|
|
debug!("Rot normalized: {}", self.offset); |
|
|
|
|
|
|
|
|
|
// We construct the `Init` struct here
|
|
|
|
|
Ok(Main { |
|
|
|
|
config: self, |
|
|
|
|
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<()> { |
|
|
|
|
// Using custom version (default is CARGO_PKG_VERSION)
|
|
|
|
|
let version = format!( |
|
|
|
@ -148,7 +154,11 @@ fn main() -> Fallible<()> { |
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
f.read_to_string(&mut buf)?; |
|
|
|
@ -157,3 +167,20 @@ fn main() -> Fallible<()> { |
|
|
|
|
|
|
|
|
|
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() |
|
|
|
|
} |
|
|
|
|