Skip to content

Commit 3165fca

Browse files
committed
feat: Implement Serialization and Deserialization
Verbosity is serialized and deserialized using the title case of the VerbosityFilter (e.g. "Debug") The `serde` dependency is gated behind an optional feature flag. Fixes: #88
1 parent f735876 commit 3165fca

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

Cargo.lock

Lines changed: 106 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,19 @@ codecov = { repository = "clap-rs/clap-verbosity-flag" }
127127
default = ["log"]
128128
log = ["dep:log"]
129129
tracing = ["dep:tracing-core"]
130+
serde = ["dep:serde"]
130131

131132
[dependencies]
132133
clap = { version = "4.0.0", default-features = false, features = ["std", "derive"] }
133134
log = { version = "0.4.1", optional = true }
135+
serde = { version = "1", features = ["derive"], optional = true }
134136
tracing-core = { version = "0.1", optional = true }
135137

136138
[dev-dependencies]
137139
clap = { version = "4.5.4", default-features = false, features = ["help", "usage"] }
138140
env_logger = "0.11.3"
141+
serde_test = { version = "1.0.177" }
142+
toml = { version = "0.8.19" }
139143
tracing = "0.1"
140144
tracing-subscriber = "0.3"
141145

src/lib.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,19 @@ pub mod tracing;
110110
/// Logging flags to `#[command(flatten)]` into your CLI
111111
#[derive(clap::Args, Debug, Clone, Copy, Default, PartialEq, Eq)]
112112
#[command(about = None, long_about = None)]
113+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
114+
#[cfg_attr(
115+
feature = "serde",
116+
serde(
117+
from = "VerbosityFilter",
118+
into = "VerbosityFilter",
119+
bound(serialize = "L: Clone")
120+
)
121+
)]
122+
#[cfg_attr(
123+
feature = "serde",
124+
doc = r#"This type serializes to a string representation of the log level, e.g. `"Debug"`"#
125+
)]
113126
pub struct Verbosity<L: LogLevel = ErrorLevel> {
114127
#[arg(
115128
long,
@@ -245,6 +258,7 @@ pub trait LogLevel {
245258
///
246259
/// Used to calculate the log level and filter.
247260
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
261+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
248262
pub enum VerbosityFilter {
249263
Off,
250264
Error,
@@ -532,4 +546,65 @@ mod test {
532546
}
533547
}
534548
}
549+
550+
#[cfg(feature = "serde")]
551+
#[cfg(test)]
552+
mod serde_tests {
553+
use super::*;
554+
555+
use clap::Parser;
556+
use serde::{Deserialize, Serialize};
557+
558+
#[derive(Debug, Parser, Serialize, Deserialize)]
559+
struct Cli {
560+
meaning_of_life: u8,
561+
#[command(flatten)]
562+
verbosity: Verbosity<InfoLevel>,
563+
}
564+
565+
#[test]
566+
fn serialize_toml() {
567+
let cli = Cli {
568+
meaning_of_life: 42,
569+
verbosity: Verbosity::new(2, 1),
570+
};
571+
let toml = toml::to_string(&cli).unwrap();
572+
assert_eq!(toml, "meaning_of_life = 42\nverbosity = \"Debug\"\n");
573+
}
574+
575+
#[test]
576+
fn deserialize_toml() {
577+
let toml = "meaning_of_life = 42\nverbosity = \"Debug\"\n";
578+
let cli: Cli = toml::from_str(toml).unwrap();
579+
assert_eq!(cli.meaning_of_life, 42);
580+
assert_eq!(cli.verbosity.filter(), VerbosityFilter::Debug);
581+
}
582+
583+
/// Tests that the `Verbosity` can be serialized and deserialized correctly from an a token.
584+
#[test]
585+
fn serde_round_trips() {
586+
use serde_test::{assert_tokens, Token};
587+
588+
for (filter, variant) in [
589+
(VerbosityFilter::Off, "Off"),
590+
(VerbosityFilter::Error, "Error"),
591+
(VerbosityFilter::Warn, "Warn"),
592+
(VerbosityFilter::Info, "Info"),
593+
(VerbosityFilter::Debug, "Debug"),
594+
(VerbosityFilter::Trace, "Trace"),
595+
] {
596+
let tokens = &[Token::UnitVariant {
597+
name: "VerbosityFilter",
598+
variant,
599+
}];
600+
601+
// `assert_tokens` checks both serialization and deserialization.
602+
assert_tokens(&Verbosity::<OffLevel>::from(filter), tokens);
603+
assert_tokens(&Verbosity::<ErrorLevel>::from(filter), tokens);
604+
assert_tokens(&Verbosity::<WarnLevel>::from(filter), tokens);
605+
assert_tokens(&Verbosity::<InfoLevel>::from(filter), tokens);
606+
assert_tokens(&Verbosity::<DebugLevel>::from(filter), tokens);
607+
assert_tokens(&Verbosity::<TraceLevel>::from(filter), tokens);
608+
}
609+
}
535610
}

0 commit comments

Comments
 (0)