schema_registry_cli/settings/
verbosity.rs

1use clap::{ArgAction, Args};
2
3/// Verbosity
4///
5/// - `-qq`     silences output
6/// - `-q`      only shows errors
7/// - (default) shows warnings and errors
8/// - `-v`      only shows infos, warnings and errors
9/// - `-vv`     only shows debugs, infos, warnings and errors
10/// - `-vvv`    only shows traces, debugs, infos, warnings and errors
11#[derive(Debug, Clone, Copy, Default, Args)]
12pub struct Verbosity {
13    /// More outputs per occurrence
14    #[arg(long, short='v', action= ArgAction::Count, global = true)]
15    verbose: u8,
16
17    /// Less outputs per occurrence
18    #[arg(long, short='q', action= ArgAction::Count, global = true)]
19    quiet: u8,
20}
21
22impl From<Verbosity> for i16 {
23    fn from(value: Verbosity) -> Self {
24        let result = i16::from(value.verbose) - i16::from(value.quiet);
25        result.clamp(-2, 3)
26    }
27}
28
29impl PartialEq for Verbosity {
30    fn eq(&self, other: &Self) -> bool {
31        i16::from(*self) == i16::from(*other)
32    }
33}
34impl Eq for Verbosity {}
35
36impl Ord for Verbosity {
37    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
38        i16::from(*self).cmp(&i16::from(*other))
39    }
40}
41impl PartialOrd for Verbosity {
42    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
43        Some(self.cmp(other))
44    }
45}
46
47impl Verbosity {
48    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
49    const fn new(level: i16) -> Self {
50        if level == 0 {
51            Self {
52                verbose: 0,
53                quiet: 0,
54            }
55        } else if level > 0 {
56            Self {
57                verbose: level as u8,
58                quiet: 0,
59            }
60        } else {
61            Self {
62                verbose: 0,
63                quiet: level.unsigned_abs() as u8,
64            }
65        }
66    }
67
68    /// Level OFF
69    pub const OFF: Verbosity = Verbosity::new(-2);
70
71    /// Level ERROR
72    pub const ERROR: Verbosity = Verbosity::new(-1);
73
74    /// Level WARN
75    pub const WARN: Verbosity = Verbosity::new(0);
76
77    /// Level INFO
78    pub const INFO: Verbosity = Verbosity::new(1);
79
80    /// Level DEBUG
81    pub const DEBUG: Verbosity = Verbosity::new(2);
82
83    /// Level TRACE
84    pub const TRACE: Verbosity = Verbosity::new(3);
85}
86
87#[cfg(test)]
88mod tests {
89    use assert2::check;
90    use clap::Parser;
91
92    use super::*;
93
94    #[derive(Debug, Parser)]
95    struct JustVerbosity {
96        #[clap(flatten)]
97        verbosity: Verbosity,
98    }
99
100    #[rstest::rstest]
101    #[case::off(Verbosity::OFF, -2)]
102    #[case::error(Verbosity::ERROR, -1)]
103    #[case::warn(Verbosity::WARN, 0)]
104    #[case::info(Verbosity::INFO, 1)]
105    #[case::debug(Verbosity::DEBUG, 2)]
106    #[case::trace(Verbosity::TRACE, 3)]
107    fn should_provide_number(#[case] verbosity: Verbosity, #[case] num: i16) {
108        let result = i16::from(verbosity);
109        check!(result == num);
110        let result = Verbosity::new(num);
111        check!(result == verbosity);
112    }
113
114    #[rstest::rstest]
115    #[case::trace(&["bin", "-vvvvvvvvvvv"], Verbosity::TRACE)]
116    #[case::trace(&["bin", "-vvv"], Verbosity::TRACE)]
117    #[case::debug(&["bin", "-vv"], Verbosity::DEBUG)]
118    #[case::info(&["bin", "-v"], Verbosity::INFO)]
119    #[case::warn(&["bin"], Verbosity::WARN)]
120    #[case::error(&["bin", "-q"], Verbosity::ERROR)]
121    #[case::off(&["bin", "-qq"], Verbosity::OFF)]
122    #[case::off(&["bin", "-qqqqqqqq"], Verbosity::OFF)]
123    fn should_parse_verbosity(#[case] args: &[&str], #[case] expected: Verbosity) {
124        let result = JustVerbosity::parse_from(args);
125        check!(result.verbosity == expected);
126    }
127
128    #[test]
129    fn should_be_sorted() {
130        let v = &[
131            Verbosity::OFF,
132            Verbosity::ERROR,
133            Verbosity::WARN,
134            Verbosity::INFO,
135            Verbosity::DEBUG,
136            Verbosity::TRACE,
137        ];
138
139        for win in v.windows(2) {
140            check!(win[0] < win[1]);
141            check!(i16::from(win[0]) < i16::from(win[1]));
142        }
143    }
144}