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}