Skip to main content

oxigaf_cli/
verbosity.rs

1//! Verbosity level configuration for CLI output.
2//!
3//! Controls logging, progress bars, and timing information based on
4//! command-line flags (-v, -vv, -vvv, -q).
5
6use tracing::Level;
7
8/// Verbosity levels for CLI output.
9///
10/// Determines logging detail, progress bar visibility, and timing information.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
12pub enum Verbosity {
13    /// Only errors (-q).
14    ///
15    /// Suppresses all output except errors. No progress bars or informational messages.
16    Quiet,
17
18    /// Progress + results (default).
19    ///
20    /// Shows progress bars, informational messages, and results.
21    Normal,
22
23    /// Debug info (-v).
24    ///
25    /// Includes timing information and debug-level logging.
26    Verbose,
27
28    /// Trace-level logging (-vv).
29    ///
30    /// Enables trace-level logging with file and line information.
31    Debug,
32
33    /// All internal details (-vvv).
34    ///
35    /// Maximum verbosity with all internal details and trace logging.
36    Trace,
37}
38
39impl Verbosity {
40    /// Create verbosity level from command-line flags.
41    ///
42    /// # Arguments
43    ///
44    /// * `verbose` - Number of `-v` flags (0 = normal, 1 = verbose, 2 = debug, 3+ = trace)
45    /// * `quiet` - Whether `-q` flag was specified (overrides verbose)
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use oxigaf_cli::verbosity::Verbosity;
51    ///
52    /// let quiet = Verbosity::from_flags(0, true);
53    /// assert_eq!(quiet, Verbosity::Quiet);
54    ///
55    /// let normal = Verbosity::from_flags(0, false);
56    /// assert_eq!(normal, Verbosity::Normal);
57    ///
58    /// let verbose = Verbosity::from_flags(1, false);
59    /// assert_eq!(verbose, Verbosity::Verbose);
60    ///
61    /// let trace = Verbosity::from_flags(3, false);
62    /// assert_eq!(trace, Verbosity::Trace);
63    /// ```
64    #[must_use]
65    pub fn from_flags(verbose: u8, quiet: bool) -> Self {
66        if quiet {
67            Self::Quiet
68        } else {
69            match verbose {
70                0 => Self::Normal,
71                1 => Self::Verbose,
72                2 => Self::Debug,
73                _ => Self::Trace,
74            }
75        }
76    }
77
78    /// Get the tracing level for this verbosity.
79    ///
80    /// Maps verbosity to appropriate `tracing::Level`:
81    /// - Quiet → ERROR
82    /// - Normal → INFO
83    /// - Verbose → DEBUG
84    /// - Debug/Trace → TRACE
85    #[must_use]
86    pub fn tracing_level(&self) -> Level {
87        match self {
88            Self::Quiet => Level::ERROR,
89            Self::Normal => Level::INFO,
90            Self::Verbose => Level::DEBUG,
91            Self::Debug | Self::Trace => Level::TRACE,
92        }
93    }
94
95    /// Whether progress bars should be shown.
96    ///
97    /// Progress bars are shown in Normal and Verbose modes, but hidden
98    /// in Quiet, Debug, and Trace modes (where detailed logging is preferred).
99    #[must_use]
100    pub fn show_progress(&self) -> bool {
101        matches!(self, Self::Normal | Self::Verbose)
102    }
103
104    /// Whether detailed timing information should be shown.
105    ///
106    /// Timing information is shown in Verbose, Debug, and Trace modes.
107    #[must_use]
108    pub fn show_timing(&self) -> bool {
109        *self >= Self::Verbose
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_from_flags_quiet() {
119        let v = Verbosity::from_flags(0, true);
120        assert_eq!(v, Verbosity::Quiet);
121
122        // Quiet overrides verbose flag
123        let v = Verbosity::from_flags(3, true);
124        assert_eq!(v, Verbosity::Quiet);
125    }
126
127    #[test]
128    fn test_from_flags_normal() {
129        let v = Verbosity::from_flags(0, false);
130        assert_eq!(v, Verbosity::Normal);
131    }
132
133    #[test]
134    fn test_from_flags_verbose_levels() {
135        let v1 = Verbosity::from_flags(1, false);
136        assert_eq!(v1, Verbosity::Verbose);
137
138        let v2 = Verbosity::from_flags(2, false);
139        assert_eq!(v2, Verbosity::Debug);
140
141        let v3 = Verbosity::from_flags(3, false);
142        assert_eq!(v3, Verbosity::Trace);
143
144        let v4 = Verbosity::from_flags(10, false);
145        assert_eq!(v4, Verbosity::Trace);
146    }
147
148    #[test]
149    fn test_tracing_level() {
150        assert_eq!(Verbosity::Quiet.tracing_level(), Level::ERROR);
151        assert_eq!(Verbosity::Normal.tracing_level(), Level::INFO);
152        assert_eq!(Verbosity::Verbose.tracing_level(), Level::DEBUG);
153        assert_eq!(Verbosity::Debug.tracing_level(), Level::TRACE);
154        assert_eq!(Verbosity::Trace.tracing_level(), Level::TRACE);
155    }
156
157    #[test]
158    fn test_show_progress() {
159        assert!(!Verbosity::Quiet.show_progress());
160        assert!(Verbosity::Normal.show_progress());
161        assert!(Verbosity::Verbose.show_progress());
162        assert!(!Verbosity::Debug.show_progress());
163        assert!(!Verbosity::Trace.show_progress());
164    }
165
166    #[test]
167    fn test_show_timing() {
168        assert!(!Verbosity::Quiet.show_timing());
169        assert!(!Verbosity::Normal.show_timing());
170        assert!(Verbosity::Verbose.show_timing());
171        assert!(Verbosity::Debug.show_timing());
172        assert!(Verbosity::Trace.show_timing());
173    }
174
175    #[test]
176    fn test_ordering() {
177        assert!(Verbosity::Quiet < Verbosity::Normal);
178        assert!(Verbosity::Normal < Verbosity::Verbose);
179        assert!(Verbosity::Verbose < Verbosity::Debug);
180        assert!(Verbosity::Debug < Verbosity::Trace);
181    }
182}