Skip to content

Commit

Permalink
feat!: Make log crate optional
Browse files Browse the repository at this point in the history
- Add log feature flag (enabled by default)
- Move log specific code to `log` module
- Move top level re-exports of log types to `log` module

BREAKING CHANGE: The log crate is now an optional dependency, enabled by
default. The `log::Level` and `log::LevelFilter` types are now
re-exported from the `log` module rather than the crate root. If you
were using these types directly, you will need to update your imports.

```diff
-use clap_verbosity_flag::{Level, LevelFilter};
+use clap_verbosity_flag::log::{Level, LevelFilter};
```
  • Loading branch information
joshka committed Nov 18, 2024
1 parent ffffbe5 commit e665c5b
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 94 deletions.
18 changes: 17 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,12 @@ pre-release-replacements = [
[badges]
codecov = { repository = "clap-rs/clap-verbosity-flag" }

[features]
default = ["log"]
log = ["dep:log"]

[dependencies]
log = "0.4.1"
log = { version = "0.4.1", optional = true }
clap = { version = "4.0.0", default-features = false, features = ["std", "derive"] }

[dev-dependencies]
Expand All @@ -126,3 +130,15 @@ tracing-log = "0.2"

[lints]
workspace = true

[[example]]
name = "log"
required-features = ["log"]

[[example]]
name = "log_level"
required-features = ["log"]

[[example]]
name = "tracing"
required-features = ["log"]
110 changes: 17 additions & 93 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
//! # verbose: Verbosity,
//! # }
//! let cli = Cli::parse();
//! # #[cfg(feature = "log")]
//! env_logger::Builder::new()
//! .filter_level(cli.verbose.log_level_filter())
//! .init();
Expand Down Expand Up @@ -63,8 +64,8 @@

use std::fmt;

pub use log::Level;
pub use log::LevelFilter;
#[cfg(feature = "log")]
pub mod log;

/// Logging flags to `#[command(flatten)]` into your CLI
#[derive(clap::Args, Debug, Clone, Default)]
Expand Down Expand Up @@ -111,18 +112,6 @@ impl<L: LogLevel> Verbosity<L> {
self.verbose != 0 || self.quiet != 0
}

/// Get the log level.
///
/// `None` means all output is disabled.
pub fn log_level(&self) -> Option<Level> {
self.filter().into()
}

/// Get the log level filter.
pub fn log_level_filter(&self) -> LevelFilter {
self.filter().into()
}

/// If the user requested complete silence (i.e. not just no-logging).
pub fn is_silent(&self) -> bool {
self.filter() == VerbosityFilter::Off
Expand All @@ -133,6 +122,20 @@ impl<L: LogLevel> Verbosity<L> {
let offset = self.verbose as i16 - self.quiet as i16;
L::default_filter().with_offset(offset)
}

/// Get the log level.
///
/// `None` means all output is disabled.
#[cfg(feature = "log")]
pub fn log_level(&self) -> Option<log::Level> {
self.filter().into()
}

/// Get the log level filter.
#[cfg(feature = "log")]
pub fn log_level_filter(&self) -> log::LevelFilter {
self.filter().into()
}
}

impl<L: LogLevel> fmt::Display for Verbosity<L> {
Expand Down Expand Up @@ -217,58 +220,6 @@ impl fmt::Display for VerbosityFilter {
}
}

impl From<VerbosityFilter> for LevelFilter {
fn from(filter: VerbosityFilter) -> Self {
match filter {
VerbosityFilter::Off => LevelFilter::Off,
VerbosityFilter::Error => LevelFilter::Error,
VerbosityFilter::Warn => LevelFilter::Warn,
VerbosityFilter::Info => LevelFilter::Info,
VerbosityFilter::Debug => LevelFilter::Debug,
VerbosityFilter::Trace => LevelFilter::Trace,
}
}
}

impl From<LevelFilter> for VerbosityFilter {
fn from(level: LevelFilter) -> Self {
match level {
LevelFilter::Off => Self::Off,
LevelFilter::Error => Self::Error,
LevelFilter::Warn => Self::Warn,
LevelFilter::Info => Self::Info,
LevelFilter::Debug => Self::Debug,
LevelFilter::Trace => Self::Trace,
}
}
}

impl From<VerbosityFilter> for Option<Level> {
fn from(filter: VerbosityFilter) -> Self {
match filter {
VerbosityFilter::Off => None,
VerbosityFilter::Error => Some(Level::Error),
VerbosityFilter::Warn => Some(Level::Warn),
VerbosityFilter::Info => Some(Level::Info),
VerbosityFilter::Debug => Some(Level::Debug),
VerbosityFilter::Trace => Some(Level::Trace),
}
}
}

impl From<Option<Level>> for VerbosityFilter {
fn from(level: Option<Level>) -> Self {
match level {
None => Self::Off,
Some(Level::Error) => Self::Error,
Some(Level::Warn) => Self::Warn,
Some(Level::Info) => Self::Info,
Some(Level::Debug) => Self::Debug,
Some(Level::Trace) => Self::Trace,
}
}
}

/// Default to [`VerbosityFilter::Error`]
#[derive(Copy, Clone, Debug, Default)]
pub struct ErrorLevel;
Expand Down Expand Up @@ -345,33 +296,6 @@ mod test {
Cli::command().debug_assert();
}

#[test]
fn log_level() {
let v = Verbosity::<OffLevel>::default();
assert_eq!(v.log_level(), None);
assert_eq!(v.log_level_filter(), LevelFilter::Off);

let v = Verbosity::<ErrorLevel>::default();
assert_eq!(v.log_level(), Some(Level::Error));
assert_eq!(v.log_level_filter(), LevelFilter::Error);

let v = Verbosity::<WarnLevel>::default();
assert_eq!(v.log_level(), Some(Level::Warn));
assert_eq!(v.log_level_filter(), LevelFilter::Warn);

let v = Verbosity::<InfoLevel>::default();
assert_eq!(v.log_level(), Some(Level::Info));
assert_eq!(v.log_level_filter(), LevelFilter::Info);

let v = Verbosity::<DebugLevel>::default();
assert_eq!(v.log_level(), Some(Level::Debug));
assert_eq!(v.log_level_filter(), LevelFilter::Debug);

let v = Verbosity::<TraceLevel>::default();
assert_eq!(v.log_level(), Some(Level::Trace));
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
}

/// Asserts that the filter is correct for the given verbosity and quiet values.
#[track_caller]
fn assert_filter<L: LogLevel>(verbose: u8, quiet: u8, expected: VerbosityFilter) {
Expand Down
91 changes: 91 additions & 0 deletions src/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// These re-exports of the log crate make it easy to use this crate without having to depend on the
// log crate directly. See <https://github.com/clap-rs/clap-verbosity-flag/issues/54> for more
// information.
pub use log::{Level, LevelFilter};

use crate::VerbosityFilter;

impl From<VerbosityFilter> for LevelFilter {
fn from(filter: VerbosityFilter) -> Self {
match filter {
VerbosityFilter::Off => LevelFilter::Off,
VerbosityFilter::Error => LevelFilter::Error,
VerbosityFilter::Warn => LevelFilter::Warn,
VerbosityFilter::Info => LevelFilter::Info,
VerbosityFilter::Debug => LevelFilter::Debug,
VerbosityFilter::Trace => LevelFilter::Trace,
}
}
}

impl From<LevelFilter> for VerbosityFilter {
fn from(level: LevelFilter) -> Self {
match level {
LevelFilter::Off => Self::Off,
LevelFilter::Error => Self::Error,
LevelFilter::Warn => Self::Warn,
LevelFilter::Info => Self::Info,
LevelFilter::Debug => Self::Debug,
LevelFilter::Trace => Self::Trace,
}
}
}

impl From<VerbosityFilter> for Option<Level> {
fn from(filter: VerbosityFilter) -> Self {
match filter {
VerbosityFilter::Off => None,
VerbosityFilter::Error => Some(Level::Error),
VerbosityFilter::Warn => Some(Level::Warn),
VerbosityFilter::Info => Some(Level::Info),
VerbosityFilter::Debug => Some(Level::Debug),
VerbosityFilter::Trace => Some(Level::Trace),
}
}
}

impl From<Option<Level>> for VerbosityFilter {
fn from(level: Option<Level>) -> Self {
match level {
None => Self::Off,
Some(Level::Error) => Self::Error,
Some(Level::Warn) => Self::Warn,
Some(Level::Info) => Self::Info,
Some(Level::Debug) => Self::Debug,
Some(Level::Trace) => Self::Trace,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{DebugLevel, ErrorLevel, InfoLevel, OffLevel, TraceLevel, Verbosity, WarnLevel};

#[test]
fn log_level() {
let v = Verbosity::<OffLevel>::default();
assert_eq!(v.log_level(), None);
assert_eq!(v.log_level_filter(), LevelFilter::Off);

let v = Verbosity::<ErrorLevel>::default();
assert_eq!(v.log_level(), Some(Level::Error));
assert_eq!(v.log_level_filter(), LevelFilter::Error);

let v = Verbosity::<WarnLevel>::default();
assert_eq!(v.log_level(), Some(Level::Warn));
assert_eq!(v.log_level_filter(), LevelFilter::Warn);

let v = Verbosity::<InfoLevel>::default();
assert_eq!(v.log_level(), Some(Level::Info));
assert_eq!(v.log_level_filter(), LevelFilter::Info);

let v = Verbosity::<DebugLevel>::default();
assert_eq!(v.log_level(), Some(Level::Debug));
assert_eq!(v.log_level_filter(), LevelFilter::Debug);

let v = Verbosity::<TraceLevel>::default();
assert_eq!(v.log_level(), Some(Level::Trace));
assert_eq!(v.log_level_filter(), LevelFilter::Trace);
}
}

0 comments on commit e665c5b

Please sign in to comment.