cli_core/
lib.rs

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    // `()` indicates that the macro takes no argument.
17    () => {
18        // The macro will expand into the contents of this block.
19        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}