1#[macro_use]
2extern crate serde_derive;
3extern crate console;
4extern crate docopt;
5extern crate flexi_logger;
6extern crate serde;
7
8pub use console::{style, Style, StyledObject};
9use docopt::Docopt;
10use flexi_logger::{Level, LevelFilter, LogSpecification, Logger, Record};
11use serde::de::Deserialize;
12use std::io;
13
14#[macro_export]
15macro_rules! cargo_version {
16 () => {
18 format!(
20 "{}.{}.{}{}",
21 env!("CARGO_PKG_VERSION_MAJOR"),
22 env!("CARGO_PKG_VERSION_MINOR"),
23 env!("CARGO_PKG_VERSION_PATCH"),
24 option_env!("CARGO_PKG_VERSION_PRE").unwrap_or("")
25 );
26 };
27}
28
29#[derive(PartialEq, Deserialize, Debug)]
30pub enum ColorOption {
31 Auto,
32 Always,
33 Never,
34}
35
36pub trait Options {
37 fn debug(&self) -> bool {
38 self.verbose()
39 }
40
41 fn verbose(&self) -> bool {
42 false
43 }
44
45 fn color(&self) -> &ColorOption {
46 &ColorOption::Auto
47 }
48}
49
50pub fn get_options<'a, T>(usage: &str) -> io::Result<T>
51where
52 T: Deserialize<'a> + Options,
53{
54 Docopt::new(usage)
55 .and_then(|d| Ok(d.version(Some(cargo_version!()))))
56 .and_then(|d| Ok(d.options_first(true)))
57 .and_then(|d| d.deserialize())
58 .map_err(|e| e.exit())
59 .and_then(|o| {
60 set_colors_enabled(&o);
61 set_loglevel(&o);
62 Ok(o)
63 })
64}
65
66fn set_colors_enabled<T>(options: &T)
67where
68 T: Options,
69{
70 match *options.color() {
71 ColorOption::Never => console::set_colors_enabled(false),
72 ColorOption::Always => console::set_colors_enabled(true),
73 ColorOption::Auto => (),
74 };
75}
76
77fn set_loglevel<T>(options: &T)
78where
79 T: Options,
80{
81 let log_spec_builder = if options.debug() {
82 LogSpecification::default(LevelFilter::max())
83 } else if options.verbose() {
84 LogSpecification::default(LevelFilter::Info)
85 } else {
86 LogSpecification::default(LevelFilter::Warn)
87 };
88
89 let log_spec = log_spec_builder.build();
90
91 Logger::with(log_spec).format(format_logs).start().unwrap();
92}
93
94fn format_logs(writer: &mut io::Write, record: &Record) -> Result<(), io::Error> {
95 let style = match record.level() {
96 Level::Trace => Style::new().white().dim().italic(),
97 Level::Debug => Style::new().white().dim(),
98 Level::Info => Style::new().white(),
99 Level::Warn => Style::new().yellow(),
100 Level::Error => Style::new().red(),
101 };
102
103 writer
104 .write(&format!("{}", style.apply_to(record.args())).into_bytes())
105 .map(|_| ())
106}