Skip to main content

pistonite_cu/
lv.rs

1//! # Logging
2//!
3//! *Does not require any feature flag*
4//!
5//! The logging macros (`debug`, `info`, `trace`, `warn`, `error`) are
6//! re-exported from the [`log`](https://docs.rs/log) crate and are
7//! used as `cu::debug!`, `cu::info!`, etc. This means your log statements
8//! are integrated into the log infrastructure when you use `cu` in a library.
9//!
10//! Additionally, the [`cu::fmtand!`](macro@crate::fmtand) and
11//! [`cu::panicand!`](macro@crate::panicand) macros allow you
12//! to log a message in additional to `format!`/`panic!`.
13//!
14//! When the `cli` feature is enabled, you also get log integration
15//! with CLI flags and other terminal-printing features.
16//! See [Command Line Interface](mod@crate::cli)
17//!
18//! `cu::lv` shorthands should be used within this library (for example
19//! `cu::lv::D` for debug). You can also call `.into()` to convert it
20//! to `log::Level`. Additionally `cu::lv::LogLevel` is a re-export of `log::Level`.
21
22pub use log::{Level as LogLevel, Record as LogRecord, debug, error, info, trace, warn};
23
24use std::sync::atomic::{AtomicBool, Ordering};
25
26use cu::Atomic;
27
28/// Format and invoke a print macro
29///
30/// # Example
31/// ```rust
32/// # use pistonite_cu as cu;
33/// let x = cu::fmtand!(error!("found {} errors", 3));
34/// assert_eq!(x, "found 3 errors");
35/// ```
36#[macro_export]
37macro_rules! fmtand {
38    ($mac:ident !( $($fmt_args:tt)* )) => {{
39        let s = format!($($fmt_args)*);
40        $crate::$mac!("{s}");
41        s
42    }}
43}
44
45pub(crate) static PRINT_LEVEL: Atomic<u8, Print> = Atomic::new_u8(Print::Normal as u8);
46pub(crate) static USE_COLOR: AtomicBool = AtomicBool::new(true);
47
48/// Get if color printing is enabled **Only works when cu::cli is being used**.
49pub fn color_enabled() -> bool {
50    USE_COLOR.load(Ordering::Acquire)
51}
52
53static ENABLE_TRACE_HINT: AtomicBool = AtomicBool::new(true);
54static ENABLE_PRINT_TIME: AtomicBool = AtomicBool::new(true);
55
56/// Disable printing the trace hint line if the CLI entry point returns an error
57///
58/// By default, the hint is displayed if `RUST_BACKTRACE` env var is not set
59#[inline(always)]
60#[cfg(feature = "print")]
61pub fn disable_trace_hint() {
62    ENABLE_TRACE_HINT.store(false, Ordering::Release);
63}
64
65/// Check if the "use -vv to display backtrace" will be printed on error.
66/// **Only works when cu::cli is being used**
67#[inline(always)]
68pub fn is_trace_hint_enabled() -> bool {
69    ENABLE_TRACE_HINT.load(Ordering::Acquire)
70}
71
72/// Disable printing the time took to run the command
73#[inline(always)]
74#[cfg(feature = "print")]
75pub fn disable_print_time() {
76    ENABLE_PRINT_TIME.store(false, Ordering::Release);
77}
78
79/// Check if the "finished in TIME" line will be printed on exit.
80/// **Only works when cu::cli is being used**
81#[inline(always)]
82pub fn is_print_time_enabled() -> bool {
83    ENABLE_PRINT_TIME.load(Ordering::Acquire)
84}
85
86/// Color Level settable with `--color` flag
87#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
88#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
89pub enum Color {
90    Always,
91    Never,
92    #[default]
93    Auto,
94}
95impl std::fmt::Display for Color {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match self {
98            Self::Always => write!(f, "always"),
99            Self::Never => write!(f, "never"),
100            Self::Auto => write!(f, "auto"),
101        }
102    }
103}
104impl Color {
105    /// Get if color should be used. If `Auto`, returns if stdout is terminal.
106    pub fn is_colored_for_stdout(self) -> bool {
107        use std::io::IsTerminal;
108        match self {
109            Self::Always => true,
110            Self::Never => false,
111            Self::Auto => std::io::stdout().is_terminal(),
112        }
113    }
114
115    /// Return the first `--color <COLOR>` or `--color=<COLOR>`
116    /// found in os args
117    pub fn from_os_args() -> Self {
118        // for efficiency, we always return the first one
119        let mut found_color = false;
120        for x in std::env::args() {
121            if found_color {
122                if x == "always" {
123                    return Self::Always;
124                }
125                if x == "never" {
126                    return Self::Never;
127                }
128                if x == "auto" {
129                    return Self::Auto;
130                }
131                found_color = false;
132                continue;
133            }
134            if x == "--color" {
135                found_color = true;
136                continue;
137            }
138            if x == "--color=always" {
139                return Self::Always;
140            }
141            if x == "--color=never" {
142                return Self::Never;
143            }
144            if x == "--color=auto" {
145                return Self::Auto;
146            }
147        }
148        Self::Auto
149    }
150}
151
152/// Print level settable with `-v` and `-q` flags
153#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
154#[repr(u8)]
155pub enum Print {
156    QuietQuiet,
157    Quiet,
158    Normal,
159    Verbose,
160    VerboseVerbose,
161}
162impl From<i8> for Print {
163    fn from(value: i8) -> Self {
164        match value {
165            ..=-2 => Self::QuietQuiet,
166            -1 => Self::Quiet,
167            0 => Self::Normal,
168            1 => Self::Verbose,
169            2.. => Self::VerboseVerbose,
170        }
171    }
172}
173impl From<u8> for Print {
174    fn from(value: u8) -> Self {
175        match value {
176            0 => Self::QuietQuiet,
177            1 => Self::Quiet,
178            3 => Self::Verbose,
179            4 => Self::VerboseVerbose,
180            _ => Self::Normal,
181        }
182    }
183}
184impl From<Print> for u8 {
185    fn from(value: Print) -> Self {
186        value as Self
187    }
188}
189impl From<Print> for log::LevelFilter {
190    fn from(value: Print) -> Self {
191        match value {
192            Print::QuietQuiet => log::LevelFilter::Off,
193            Print::Quiet => log::LevelFilter::Error,
194            Print::Normal => log::LevelFilter::Info,
195            Print::Verbose => log::LevelFilter::Debug,
196            Print::VerboseVerbose => log::LevelFilter::Trace,
197        }
198    }
199}
200
201/// Prompt level set with `--yes`, `--interactive`, and `--non-interactive` flags
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203#[repr(u8)]
204pub enum Prompt {
205    /// Show all prompts interactively
206    Interactive,
207    /// Automatically answer "Yes" to all yes/no prompts, and show other prompts interactively
208    YesOrInteractive,
209    /// Automatically answer "Yes" to all yes/no prompts, and do not allow other prompts
210    YesOrBlock,
211    /// Do not allow any type of prompts (non-interactive). Attempting to show prompt will error
212    Block,
213}
214impl From<u8> for Prompt {
215    fn from(value: u8) -> Self {
216        match value {
217            1 => Self::YesOrInteractive,
218            2 => Self::YesOrBlock,
219            3 => Self::Block,
220            _ => Self::Interactive,
221        }
222    }
223}
224impl From<Prompt> for u8 {
225    fn from(value: Prompt) -> Self {
226        value as Self
227    }
228}
229
230/// Level of a message/print event.
231///
232/// Shortcuts available at `cu::lv`, e.g. `cu::lv::E` is `Error`
233#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
234#[repr(u8)]
235pub enum Lv {
236    Error,
237    Hint,
238    Print,
239    Warn,
240    Info,
241    Debug,
242    Trace,
243
244    Off,
245}
246impl Lv {
247    /// Check if the logging level is currently enabled **Only works when cu::cli is being used**.
248    ///
249    /// ```rust
250    /// # use pistonite_cu as cu;
251    /// // check that INFO level is enabled
252    /// assert!(cu::lv::I.enabled());
253    /// ```
254    #[inline(always)]
255    pub fn enabled(self) -> bool {
256        self.can_print(PRINT_LEVEL.get())
257    }
258    /// Check if a print level can print this message level
259    pub fn can_print(self, level: Print) -> bool {
260        match self {
261            Lv::Off => false,
262            Lv::Error | Lv::Hint | Lv::Print => level != Print::QuietQuiet,
263            Lv::Warn | Lv::Info => level > Print::Quiet,
264            Lv::Debug => level > Print::Normal,
265            Lv::Trace => level == Print::VerboseVerbose,
266        }
267    }
268}
269impl From<log::Level> for Lv {
270    fn from(value: log::Level) -> Self {
271        match value {
272            log::Level::Error => Self::Error,
273            log::Level::Warn => Self::Warn,
274            log::Level::Info => Self::Info,
275            log::Level::Debug => Self::Debug,
276            log::Level::Trace => Self::Trace,
277        }
278    }
279}
280impl From<Lv> for log::Level {
281    fn from(value: Lv) -> Self {
282        match value {
283            Lv::Error => log::Level::Error,
284            Lv::Hint => log::Level::Warn,
285            Lv::Print => log::Level::Info,
286            Lv::Warn => log::Level::Warn,
287            Lv::Info => log::Level::Info,
288            Lv::Debug => log::Level::Debug,
289            Lv::Trace => log::Level::Trace,
290            Lv::Off => log::Level::Error,
291        }
292    }
293}
294impl From<u8> for Lv {
295    fn from(value: u8) -> Self {
296        match value {
297            0 => Lv::Error,
298            1 => Lv::Hint,
299            2 => Lv::Print,
300            3 => Lv::Warn,
301            4 => Lv::Info,
302            5 => Lv::Debug,
303            6 => Lv::Trace,
304            _ => Lv::Off,
305        }
306    }
307}
308impl From<Lv> for u8 {
309    fn from(value: Lv) -> Self {
310        value as u8
311    }
312}
313
314impl std::fmt::Display for Lv {
315    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
316        match self {
317            Lv::Error => "error".fmt(f),
318            Lv::Hint => "hint".fmt(f),
319            Lv::Print => "print".fmt(f),
320            Lv::Warn => "warn".fmt(f),
321            Lv::Info => "info".fmt(f),
322            Lv::Debug => "debug".fmt(f),
323            Lv::Trace => "trace".fmt(f),
324            Lv::Off => "off".fmt(f),
325        }
326    }
327}
328
329/// Error
330pub const E: Lv = Lv::Error;
331/// Hint
332pub const H: Lv = Lv::Hint;
333/// Print
334pub const P: Lv = Lv::Print;
335/// Warn
336pub const W: Lv = Lv::Warn;
337/// Info
338pub const I: Lv = Lv::Info;
339/// Debug
340pub const D: Lv = Lv::Debug;
341/// Trace
342pub const T: Lv = Lv::Trace;