diff --git a/src/lib.rs b/src/lib.rs index 6ba8f3d..35cb16f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,85 +2,105 @@ //! //! # Examples //! +//! To get `--quiet` and `--verbose` flags through your entire program, just `flatten` +//! [`Verbosity`]: //! ```rust,no_run -//! use clap::Parser; -//! use clap_verbosity_flag::Verbosity; -//! -//! /// Le CLI -//! #[derive(Debug, Parser)] -//! struct Cli { -//! #[clap(flatten)] -//! verbose: Verbosity, -//! } +//! # use clap::Parser; +//! # use clap_verbosity_flag::Verbosity; +//! # +//! # /// Le CLI +//! # #[derive(Debug, Parser)] +//! # struct Cli { +//! #[clap(flatten)] +//! verbose: Verbosity, +//! # } +//! ``` //! +//! You can then use this to configure your logger: +//! ```rust,no_run +//! # use clap::Parser; +//! # use clap_verbosity_flag::Verbosity; +//! # +//! # /// Le CLI +//! # #[derive(Debug, Parser)] +//! # struct Cli { +//! # #[clap(flatten)] +//! # verbose: Verbosity, +//! # } //! let cli = Cli::parse(); //! env_logger::Builder::new() //! .filter_level(cli.verbose.log_level_filter()) //! .init(); //! ``` //! -//! This will only report errors. +//! By default, this will only report errors. //! - `-q` silences output //! - `-v` show warnings //! - `-vv` show info //! - `-vvv` show debug //! - `-vvvv` show trace - -use log::Level; -use log::LevelFilter; +//! +//! You can also customize the default logging level: +//! ```rust,no_run +//! # use clap::Parser; +//! use clap_verbosity_flag::{Verbosity, InfoLevel}; +//! +//! /// Le CLI +//! #[derive(Debug, Parser)] +//! struct Cli { +//! #[clap(flatten)] +//! verbose: Verbosity, +//! } +//! ``` +//! +//! Or implement [`LogLevel`] yourself for more control. #[derive(clap::Args, Debug, Clone)] -pub struct Verbosity { - /// Pass many times for more log output - /// - /// By default, it'll only report errors. Passing `-v` one time also prints - /// warnings, `-vv` enables info logging, `-vvv` debug, and `-vvvv` trace. - #[clap(long, short = 'v', parse(from_occurrences), global = true)] +pub struct Verbosity { + #[clap( + long, + short = 'v', + parse(from_occurrences), + global = true, + help = L::verbose_help(), + long_help = L::verbose_long_help(), + )] verbose: i8, - /// Pass many times for less log output #[clap( long, short = 'q', parse(from_occurrences), global = true, - conflicts_with = "verbose" + help = L::quiet_help(), + long_help = L::quiet_long_help(), + conflicts_with = "verbose", )] quiet: i8, #[clap(skip)] - default: i8, + phantom: std::marker::PhantomData, } -impl Verbosity { +impl Verbosity { /// Create a new verbosity instance by explicitly setting the values - pub fn new(verbose: i8, quiet: i8, default: i8) -> Verbosity { + pub fn new(verbose: i8, quiet: i8) -> Self { Verbosity { verbose, quiet, - default, + phantom: std::marker::PhantomData, } } - /// Change the default level. - /// - /// When the level is lower than `log::Error` (the default), multiple `-q`s will be needed for - /// complete silence - /// - /// `None` means all output is disabled. - pub fn set_default(&mut self, level: Option) { - self.default = level_value(level); - } - /// Get the log level. /// /// `None` means all output is disabled. - pub fn log_level(&self) -> Option { + pub fn log_level(&self) -> Option { level_enum(self.verbosity()) } /// Get the log level filter. - pub fn log_level_filter(&self) -> LevelFilter { + pub fn log_level_filter(&self) -> log::LevelFilter { level_enum(self.verbosity()) .map(|l| l.to_level_filter()) .unwrap_or(log::LevelFilter::Off) @@ -92,11 +112,11 @@ impl Verbosity { } fn verbosity(&self) -> i8 { - self.default - self.quiet + self.verbose + level_value(L::default()) - self.quiet + self.verbose } } -fn level_value(level: Option) -> i8 { +fn level_value(level: Option) -> i8 { match level { None => -1, Some(log::Level::Error) => 0, @@ -107,7 +127,7 @@ fn level_value(level: Option) -> i8 { } } -fn level_enum(verbosity: i8) -> Option { +fn level_enum(verbosity: i8) -> Option { match verbosity { std::i8::MIN..=-1 => None, 0 => Some(log::Level::Error), @@ -120,19 +140,66 @@ fn level_enum(verbosity: i8) -> Option { use std::fmt; -impl fmt::Display for Verbosity { +impl fmt::Display for Verbosity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.verbosity()) } } +pub trait LogLevel { + fn default() -> Option; + + fn verbose_help() -> Option<&'static str> { + Some("More output per occurrence") + } + + fn verbose_long_help() -> Option<&'static str> { + None + } + + fn quiet_help() -> Option<&'static str> { + Some("Less output per occurrence") + } + + fn quiet_long_help() -> Option<&'static str> { + None + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct ErrorLevel; + +impl LogLevel for ErrorLevel { + fn default() -> Option { + Some(log::Level::Error) + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct WarnLevel; + +impl LogLevel for WarnLevel { + fn default() -> Option { + Some(log::Level::Warn) + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct InfoLevel; + +impl LogLevel for InfoLevel { + fn default() -> Option { + Some(log::Level::Info) + } +} + #[cfg(test)] mod test { use super::*; #[test] fn verify_app() { - #[derive(Debug, clap::StructOpt)] + #[derive(Debug, clap::Parser)] struct Cli { #[clap(flatten)] verbose: Verbosity,