Skip to content

Commit

Permalink
Merge pull request #35 from epage/custom
Browse files Browse the repository at this point in the history
fix!: Give control over help text
  • Loading branch information
epage committed Feb 9, 2022
2 parents a2c2e40 + a7e3fbe commit 08eef70
Showing 1 changed file with 109 additions and 42 deletions.
151 changes: 109 additions & 42 deletions src/lib.rs
Expand Up @@ -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<InfoLevel>,
//! }
//! ```
//!
//! 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<L: LogLevel = ErrorLevel> {
#[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<L>,
}

impl Verbosity {
impl<L: LogLevel> Verbosity<L> {
/// 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<Level>) {
self.default = level_value(level);
}

/// Get the log level.
///
/// `None` means all output is disabled.
pub fn log_level(&self) -> Option<Level> {
pub fn log_level(&self) -> Option<log::Level> {
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)
Expand All @@ -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<Level>) -> i8 {
fn level_value(level: Option<log::Level>) -> i8 {
match level {
None => -1,
Some(log::Level::Error) => 0,
Expand All @@ -107,7 +127,7 @@ fn level_value(level: Option<Level>) -> i8 {
}
}

fn level_enum(verbosity: i8) -> Option<Level> {
fn level_enum(verbosity: i8) -> Option<log::Level> {
match verbosity {
std::i8::MIN..=-1 => None,
0 => Some(log::Level::Error),
Expand All @@ -120,19 +140,66 @@ fn level_enum(verbosity: i8) -> Option<Level> {

use std::fmt;

impl fmt::Display for Verbosity {
impl<L: LogLevel> fmt::Display for Verbosity<L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.verbosity())
}
}

pub trait LogLevel {
fn default() -> Option<log::Level>;

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<log::Level> {
Some(log::Level::Error)
}
}

#[derive(Copy, Clone, Debug, Default)]
pub struct WarnLevel;

impl LogLevel for WarnLevel {
fn default() -> Option<log::Level> {
Some(log::Level::Warn)
}
}

#[derive(Copy, Clone, Debug, Default)]
pub struct InfoLevel;

impl LogLevel for InfoLevel {
fn default() -> Option<log::Level> {
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,
Expand Down

0 comments on commit 08eef70

Please sign in to comment.