Skip to content

Commit

Permalink
feat: Enable tracing support
Browse files Browse the repository at this point in the history
Add a new `tracing` feature flag and tracing module to support the
`tracing` crate.

Fixes: clap-rs#121
  • Loading branch information
joshka committed Nov 14, 2024
1 parent ec834ec commit 0dc0249
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ codecov = { repository = "clap-rs/clap-verbosity-flag" }
[features]
default = ["log"]
log = ["dep:log"]
tracing = ["dep:tracing-core"]

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

[dev-dependencies]
clap = { version = "4.5.4", default-features = false, features = ["help", "usage"] }
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ pub use self::log::{ErrorLevel, InfoLevel, WarnLevel};
#[cfg(any(doc, feature = "log"))]
pub mod log;

#[cfg(any(doc, feature = "tracing"))]
pub mod tracing;

/// Logging flags to `#[command(flatten)]` into your CLI
#[cfg(any(doc, feature = "log"))]
#[derive(clap::Args, Debug, Clone, Default)]
Expand Down
200 changes: 200 additions & 0 deletions src/tracing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// These re-exports of the tracing types make it easy to use this crate without having to depend on
// the tracing crate directly. See <https://github.com/clap-rs/clap-verbosity-flag/issues/54> for
// more information.
pub use tracing_core::{Level, LevelFilter};

use crate::{Filter, LogLevel};

impl From<Filter> for LevelFilter {
fn from(filter: Filter) -> Self {
match filter {
Filter::Off => LevelFilter::OFF,
Filter::Error => LevelFilter::ERROR,
Filter::Warn => LevelFilter::WARN,
Filter::Info => LevelFilter::INFO,
Filter::Debug => LevelFilter::DEBUG,
Filter::Trace => LevelFilter::TRACE,
}
}
}

impl From<LevelFilter> for Filter {
fn from(level: LevelFilter) -> Self {
match level {
LevelFilter::OFF => Filter::Off,
LevelFilter::ERROR => Filter::Error,
LevelFilter::WARN => Filter::Warn,
LevelFilter::INFO => Filter::Info,
LevelFilter::DEBUG => Filter::Debug,
LevelFilter::TRACE => Filter::Trace,
}
}
}

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

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

/// Default to [`tracing_core::Level::Error`]
#[allow(clippy::exhaustive_structs)]
#[derive(Copy, Clone, Debug, Default)]
pub struct ErrorLevel;

impl LogLevel for ErrorLevel {
type Level = Level;
type LevelFilter = LevelFilter;
fn default() -> Option<Level> {
Some(Level::ERROR)
}
}

/// Default to [`tracing_core::Level::Warn`]
#[allow(clippy::exhaustive_structs)]
#[derive(Copy, Clone, Debug, Default)]
pub struct WarnLevel;

impl LogLevel for WarnLevel {
type Level = Level;
type LevelFilter = LevelFilter;
fn default() -> Option<Level> {
Some(Level::WARN)
}
}

/// Default to [`tracing_core::Level::Info`]
#[allow(clippy::exhaustive_structs)]
#[derive(Copy, Clone, Debug, Default)]
pub struct InfoLevel;

impl LogLevel for InfoLevel {
type Level = Level;
type LevelFilter = LevelFilter;
fn default() -> Option<Level> {
Some(Level::INFO)
}
}

#[cfg(test)]
mod tests {
use crate::Verbosity;

use super::*;

#[test]
fn verbosity_error_level() {
let tests = [
// verbose, quiet, expected_level, expected_filter
(0, 0, Some(Level::ERROR), LevelFilter::ERROR),
(1, 0, Some(Level::WARN), LevelFilter::WARN),
(2, 0, Some(Level::INFO), LevelFilter::INFO),
(3, 0, Some(Level::DEBUG), LevelFilter::DEBUG),
(4, 0, Some(Level::TRACE), LevelFilter::TRACE),
(5, 0, Some(Level::TRACE), LevelFilter::TRACE),
(255, 0, Some(Level::TRACE), LevelFilter::TRACE),
(0, 1, None, LevelFilter::OFF),
(0, 2, None, LevelFilter::OFF),
(0, 255, None, LevelFilter::OFF),
(255, 255, Some(Level::ERROR), LevelFilter::ERROR),
];

for (verbose, quiet, expected_level, expected_filter) in tests.iter() {
let v = Verbosity::<ErrorLevel>::new(*verbose, *quiet);
assert_eq!(
v.log_level(),
*expected_level,
"verbose = {verbose}, quiet = {quiet}"
);
assert_eq!(
v.log_level_filter(),
*expected_filter,
"verbose = {verbose}, quiet = {quiet}"
);
}
}

#[test]
fn verbosity_warn_level() {
let tests = [
// verbose, quiet, expected_level, expected_filter
(0, 0, Some(Level::WARN), LevelFilter::WARN),
(1, 0, Some(Level::INFO), LevelFilter::INFO),
(2, 0, Some(Level::DEBUG), LevelFilter::DEBUG),
(3, 0, Some(Level::TRACE), LevelFilter::TRACE),
(4, 0, Some(Level::TRACE), LevelFilter::TRACE),
(255, 0, Some(Level::TRACE), LevelFilter::TRACE),
(0, 1, Some(Level::ERROR), LevelFilter::ERROR),
(0, 2, None, LevelFilter::OFF),
(0, 3, None, LevelFilter::OFF),
(0, 255, None, LevelFilter::OFF),
(255, 255, Some(Level::WARN), LevelFilter::WARN),
];

for (verbose, quiet, expected_level, expected_filter) in tests.iter() {
let v = Verbosity::<WarnLevel>::new(*verbose, *quiet);
assert_eq!(
v.log_level(),
*expected_level,
"verbose = {verbose}, quiet = {quiet}"
);
assert_eq!(
v.log_level_filter(),
*expected_filter,
"verbose = {verbose}, quiet = {quiet}"
);
}
}

#[test]
fn verbosity_info_level() {
let tests = [
// verbose, quiet, expected_level, expected_filter
(0, 0, Some(Level::INFO), LevelFilter::INFO),
(1, 0, Some(Level::DEBUG), LevelFilter::DEBUG),
(2, 0, Some(Level::TRACE), LevelFilter::TRACE),
(3, 0, Some(Level::TRACE), LevelFilter::TRACE),
(255, 0, Some(Level::TRACE), LevelFilter::TRACE),
(0, 1, Some(Level::WARN), LevelFilter::WARN),
(0, 2, Some(Level::ERROR), LevelFilter::ERROR),
(0, 3, None, LevelFilter::OFF),
(0, 4, None, LevelFilter::OFF),
(0, 255, None, LevelFilter::OFF),
(255, 255, Some(Level::INFO), LevelFilter::INFO),
];

for (verbose, quiet, expected_level, expected_filter) in tests.iter() {
let v = Verbosity::<InfoLevel>::new(*verbose, *quiet);
assert_eq!(
v.log_level(),
*expected_level,
"verbose = {verbose}, quiet = {quiet}"
);
assert_eq!(
v.log_level_filter(),
*expected_filter,
"verbose = {verbose}, quiet = {quiet}"
);
}
}
}

0 comments on commit 0dc0249

Please sign in to comment.