uv_warnings/
lib.rs

1use std::error::Error;
2use std::iter;
3use std::sync::atomic::AtomicBool;
4use std::sync::{LazyLock, Mutex};
5
6// macro hygiene: The user might not have direct dependencies on those crates
7#[doc(hidden)]
8pub use anstream;
9#[doc(hidden)]
10pub use owo_colors;
11use owo_colors::{DynColor, OwoColorize};
12use rustc_hash::FxHashSet;
13
14/// Whether user-facing warnings are enabled.
15pub static ENABLED: AtomicBool = AtomicBool::new(false);
16
17/// Enable user-facing warnings.
18pub fn enable() {
19    ENABLED.store(true, std::sync::atomic::Ordering::Relaxed);
20}
21
22/// Disable user-facing warnings.
23pub fn disable() {
24    ENABLED.store(false, std::sync::atomic::Ordering::Relaxed);
25}
26
27/// Warn a user, if warnings are enabled.
28#[macro_export]
29macro_rules! warn_user {
30    ($($arg:tt)*) => {{
31        use $crate::anstream::eprintln;
32        use $crate::owo_colors::OwoColorize;
33
34        if $crate::ENABLED.load(std::sync::atomic::Ordering::Relaxed) {
35            let message = format!("{}", format_args!($($arg)*));
36            let formatted = message.bold();
37            eprintln!("{}{} {formatted}", "warning".yellow().bold(), ":".bold());
38        }
39    }};
40}
41
42pub static WARNINGS: LazyLock<Mutex<FxHashSet<String>>> = LazyLock::new(Mutex::default);
43
44/// Warn a user once, if warnings are enabled, with uniqueness determined by the content of the
45/// message.
46#[macro_export]
47macro_rules! warn_user_once {
48    ($($arg:tt)*) => {{
49        use $crate::anstream::eprintln;
50        use $crate::owo_colors::OwoColorize;
51
52        if $crate::ENABLED.load(std::sync::atomic::Ordering::Relaxed) {
53            if let Ok(mut states) = $crate::WARNINGS.lock() {
54                let message = format!("{}", format_args!($($arg)*));
55                if states.insert(message.clone()) {
56                    eprintln!("{}{} {}", "warning".yellow().bold(), ":".bold(), message.bold());
57                }
58            }
59        }
60    }};
61}
62
63/// Format an error or warning chain.
64///
65/// # Example
66///
67/// ```text
68/// error: Failed to install app
69///   Caused By: Failed to install dependency
70///   Caused By: Error writing failed `/home/ferris/deps/foo`: Permission denied
71/// ```
72///
73/// ```text
74/// warning: Failed to create registry entry for Python 3.12
75///   Caused By: Security policy forbids chaining registry entries
76/// ```
77pub fn write_error_chain(
78    err: &dyn Error,
79    mut stream: impl std::fmt::Write,
80    level: impl AsRef<str>,
81    color: impl DynColor + Copy,
82) -> std::fmt::Result {
83    writeln!(
84        &mut stream,
85        "{}{} {}",
86        level.as_ref().color(color).bold(),
87        ":".bold(),
88        err.to_string().trim()
89    )?;
90    for source in iter::successors(err.source(), |&err| err.source()) {
91        writeln!(
92            &mut stream,
93            "  {}: {}",
94            "Caused by".color(color).bold(),
95            source.to_string().trim()
96        )?;
97    }
98    Ok(())
99}