Skip to main content

blz_cli/args/
verbosity.rs

1//! Verbosity level configuration for CLI output.
2//!
3//! This module provides a structured way to handle output verbosity levels,
4//! replacing individual boolean flags (`quiet`, `verbose`, `debug`) with a
5//! single, well-defined enum.
6//!
7//! # Verbosity Levels
8//!
9//! From quietest to most verbose:
10//!
11//! | Level | Description | Use Case |
12//! |-------|-------------|----------|
13//! | `Quiet` | Errors only | Scripts, CI |
14//! | `Normal` | Standard output | Interactive use |
15//! | `Verbose` | Additional details | Debugging |
16//! | `Debug` | Full diagnostics | Development |
17//!
18//! **Note:** Errors are always shown regardless of verbosity level.
19//!
20//! # Examples
21//!
22//! ```
23//! use blz_cli::args::Verbosity;
24//!
25//! let level = Verbosity::default();
26//! assert_eq!(level, Verbosity::Normal);
27//!
28//! // Check if messages should be shown
29//! assert!(level.show_info());
30//! assert!(!level.show_debug());
31//! ```
32
33use clap::ValueEnum;
34use serde::{Deserialize, Serialize};
35
36/// Verbosity level for CLI output.
37///
38/// This enum provides a structured way to control output verbosity,
39/// replacing individual boolean flags with a single, clear setting.
40///
41/// # Ordering
42///
43/// Levels are ordered from quietest to most verbose:
44/// `Quiet < Normal < Verbose < Debug`
45///
46/// This allows easy comparison:
47/// ```
48/// use blz_cli::args::Verbosity;
49///
50/// assert!(Verbosity::Quiet < Verbosity::Normal);
51/// assert!(Verbosity::Verbose < Verbosity::Debug);
52/// ```
53#[derive(
54    Copy,
55    Clone,
56    Debug,
57    Default,
58    PartialEq,
59    Eq,
60    PartialOrd,
61    Ord,
62    Hash,
63    Serialize,
64    Deserialize,
65    ValueEnum,
66)]
67#[serde(rename_all = "lowercase")]
68pub enum Verbosity {
69    /// Suppress all output except errors.
70    ///
71    /// Useful for scripts and CI where only exit codes matter.
72    Quiet,
73
74    /// Standard output level (default).
75    ///
76    /// Shows results, warnings, and important information.
77    #[default]
78    Normal,
79
80    /// Show additional details.
81    ///
82    /// Includes timing information, progress indicators, and
83    /// contextual details that help understand what's happening.
84    Verbose,
85
86    /// Full diagnostic output.
87    ///
88    /// Shows all available information including performance metrics,
89    /// internal state, and debug-level logging. Primarily for
90    /// development and troubleshooting.
91    Debug,
92}
93
94impl Verbosity {
95    /// Create a Verbosity from individual boolean flags.
96    ///
97    /// This provides backward compatibility with the legacy flag pattern.
98    /// Priority order: debug > verbose > quiet > normal
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use blz_cli::args::Verbosity;
104    ///
105    /// // All false = Normal
106    /// assert_eq!(Verbosity::from_flags(false, false, false), Verbosity::Normal);
107    ///
108    /// // Quiet takes precedence when alone
109    /// assert_eq!(Verbosity::from_flags(true, false, false), Verbosity::Quiet);
110    ///
111    /// // Verbose takes precedence over quiet
112    /// assert_eq!(Verbosity::from_flags(true, true, false), Verbosity::Verbose);
113    ///
114    /// // Debug takes precedence over everything
115    /// assert_eq!(Verbosity::from_flags(true, true, true), Verbosity::Debug);
116    /// ```
117    #[must_use]
118    pub const fn from_flags(quiet: bool, verbose: bool, debug: bool) -> Self {
119        if debug {
120            Self::Debug
121        } else if verbose {
122            Self::Verbose
123        } else if quiet {
124            Self::Quiet
125        } else {
126            Self::Normal
127        }
128    }
129
130    /// Check if warning messages should be shown.
131    ///
132    /// Warnings are shown at Normal level and above.
133    #[must_use]
134    pub const fn show_warnings(self) -> bool {
135        matches!(self, Self::Normal | Self::Verbose | Self::Debug)
136    }
137
138    /// Check if informational messages should be shown.
139    ///
140    /// Info messages are shown at Normal level and above.
141    #[must_use]
142    pub const fn show_info(self) -> bool {
143        matches!(self, Self::Normal | Self::Verbose | Self::Debug)
144    }
145
146    /// Check if verbose output should be shown.
147    ///
148    /// Verbose output is shown at Verbose level and above.
149    #[must_use]
150    pub const fn show_verbose(self) -> bool {
151        matches!(self, Self::Verbose | Self::Debug)
152    }
153
154    /// Check if debug output should be shown.
155    ///
156    /// Debug output is only shown at Debug level.
157    #[must_use]
158    pub const fn show_debug(self) -> bool {
159        matches!(self, Self::Debug)
160    }
161
162    /// Check if output should be suppressed (quiet mode).
163    #[must_use]
164    pub const fn is_quiet(self) -> bool {
165        matches!(self, Self::Quiet)
166    }
167
168    /// Check if verbose mode is enabled.
169    #[must_use]
170    pub const fn is_verbose(self) -> bool {
171        matches!(self, Self::Verbose | Self::Debug)
172    }
173
174    /// Check if debug mode is enabled.
175    #[must_use]
176    pub const fn is_debug(self) -> bool {
177        matches!(self, Self::Debug)
178    }
179}
180
181impl std::fmt::Display for Verbosity {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        match self {
184            Self::Quiet => write!(f, "quiet"),
185            Self::Normal => write!(f, "normal"),
186            Self::Verbose => write!(f, "verbose"),
187            Self::Debug => write!(f, "debug"),
188        }
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn test_default_is_normal() {
198        assert_eq!(Verbosity::default(), Verbosity::Normal);
199    }
200
201    #[test]
202    fn test_ordering() {
203        assert!(Verbosity::Quiet < Verbosity::Normal);
204        assert!(Verbosity::Normal < Verbosity::Verbose);
205        assert!(Verbosity::Verbose < Verbosity::Debug);
206    }
207
208    #[test]
209    fn test_from_flags_priority() {
210        // Debug takes highest priority
211        assert_eq!(Verbosity::from_flags(true, true, true), Verbosity::Debug);
212        assert_eq!(Verbosity::from_flags(false, false, true), Verbosity::Debug);
213
214        // Verbose takes next priority
215        assert_eq!(Verbosity::from_flags(true, true, false), Verbosity::Verbose);
216        assert_eq!(
217            Verbosity::from_flags(false, true, false),
218            Verbosity::Verbose
219        );
220
221        // Quiet takes next priority
222        assert_eq!(Verbosity::from_flags(true, false, false), Verbosity::Quiet);
223
224        // Default to Normal
225        assert_eq!(
226            Verbosity::from_flags(false, false, false),
227            Verbosity::Normal
228        );
229    }
230
231    #[test]
232    fn test_show_methods() {
233        // Quiet - only errors (not controlled by these methods)
234        assert!(!Verbosity::Quiet.show_warnings());
235        assert!(!Verbosity::Quiet.show_info());
236        assert!(!Verbosity::Quiet.show_verbose());
237        assert!(!Verbosity::Quiet.show_debug());
238
239        // Normal - warnings and info
240        assert!(Verbosity::Normal.show_warnings());
241        assert!(Verbosity::Normal.show_info());
242        assert!(!Verbosity::Normal.show_verbose());
243        assert!(!Verbosity::Normal.show_debug());
244
245        // Verbose - adds verbose output
246        assert!(Verbosity::Verbose.show_warnings());
247        assert!(Verbosity::Verbose.show_info());
248        assert!(Verbosity::Verbose.show_verbose());
249        assert!(!Verbosity::Verbose.show_debug());
250
251        // Debug - shows everything
252        assert!(Verbosity::Debug.show_warnings());
253        assert!(Verbosity::Debug.show_info());
254        assert!(Verbosity::Debug.show_verbose());
255        assert!(Verbosity::Debug.show_debug());
256    }
257
258    #[test]
259    fn test_is_methods() {
260        assert!(Verbosity::Quiet.is_quiet());
261        assert!(!Verbosity::Normal.is_quiet());
262
263        assert!(!Verbosity::Quiet.is_verbose());
264        assert!(Verbosity::Verbose.is_verbose());
265        assert!(Verbosity::Debug.is_verbose());
266
267        assert!(!Verbosity::Verbose.is_debug());
268        assert!(Verbosity::Debug.is_debug());
269    }
270
271    #[test]
272    fn test_display() {
273        assert_eq!(Verbosity::Quiet.to_string(), "quiet");
274        assert_eq!(Verbosity::Normal.to_string(), "normal");
275        assert_eq!(Verbosity::Verbose.to_string(), "verbose");
276        assert_eq!(Verbosity::Debug.to_string(), "debug");
277    }
278}