Skip to main content

colorchoice/
lib.rs

1//! Global override of color control
2
3#![cfg_attr(not(test), no_std)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![warn(missing_docs)]
6#![warn(clippy::print_stderr)]
7#![warn(clippy::print_stdout)]
8
9use core::sync::atomic::{AtomicUsize, Ordering};
10
11/// Selection for overriding color output
12#[allow(clippy::exhaustive_enums)]
13#[derive(#[automatically_derived]
#[allow(clippy::exhaustive_enums)]
impl ::core::marker::Copy for ColorChoice { }Copy, #[automatically_derived]
#[allow(clippy::exhaustive_enums)]
impl ::core::clone::Clone for ColorChoice {
    #[inline]
    fn clone(&self) -> ColorChoice { *self }
}Clone, #[automatically_derived]
#[allow(clippy::exhaustive_enums)]
impl ::core::fmt::Debug for ColorChoice {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                ColorChoice::Auto => "Auto",
                ColorChoice::AlwaysAnsi => "AlwaysAnsi",
                ColorChoice::Always => "Always",
                ColorChoice::Never => "Never",
            })
    }
}Debug, #[automatically_derived]
#[allow(clippy::exhaustive_enums)]
impl ::core::cmp::PartialEq for ColorChoice {
    #[inline]
    fn eq(&self, other: &ColorChoice) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
#[allow(clippy::exhaustive_enums)]
impl ::core::cmp::Eq for ColorChoice {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
#[allow(clippy::exhaustive_enums)]
impl ::core::default::Default for ColorChoice {
    #[inline]
    fn default() -> ColorChoice { Self::Auto }
}Default)]
14pub enum ColorChoice {
15    /// Use colors if the output device appears to support them
16    #[default]
17    Auto,
18    /// Like `Always`, except it never tries to use anything other than emitting ANSI
19    /// color codes.
20    AlwaysAnsi,
21    /// Try very hard to emit colors.
22    ///
23    /// This includes emitting ANSI colors on Windows if the console API is unavailable.
24    Always,
25    /// Never emit colors.
26    Never,
27}
28
29impl ColorChoice {
30    /// Get the current [`ColorChoice`] state
31    pub fn global() -> Self {
32        USER.get()
33    }
34
35    /// Override the detected [`ColorChoice`]
36    pub fn write_global(self) {
37        USER.set(self);
38    }
39}
40
41static USER: AtomicChoice = AtomicChoice::new();
42
43#[derive(#[automatically_derived]
impl ::core::fmt::Debug for AtomicChoice {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "AtomicChoice",
            &&self.0)
    }
}Debug)]
44pub(crate) struct AtomicChoice(AtomicUsize);
45
46impl AtomicChoice {
47    pub(crate) const fn new() -> Self {
48        Self(AtomicUsize::new(Self::from_choice(ColorChoice::Auto)))
49    }
50
51    pub(crate) fn get(&self) -> ColorChoice {
52        let choice = self.0.load(Ordering::SeqCst);
53        Self::to_choice(choice).expect("Only `ColorChoice` values can be `set`")
54    }
55
56    pub(crate) fn set(&self, choice: ColorChoice) {
57        let choice = Self::from_choice(choice);
58        self.0.store(choice, Ordering::SeqCst);
59    }
60
61    const fn from_choice(choice: ColorChoice) -> usize {
62        match choice {
63            ColorChoice::Auto => 0,
64            ColorChoice::AlwaysAnsi => 1,
65            ColorChoice::Always => 2,
66            ColorChoice::Never => 3,
67        }
68    }
69
70    const fn to_choice(choice: usize) -> Option<ColorChoice> {
71        match choice {
72            0 => Some(ColorChoice::Auto),
73            1 => Some(ColorChoice::AlwaysAnsi),
74            2 => Some(ColorChoice::Always),
75            3 => Some(ColorChoice::Never),
76            _ => None,
77        }
78    }
79}
80
81impl Default for AtomicChoice {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87#[cfg(test)]
88mod test {
89    use super::*;
90
91    #[test]
92    fn choice_serialization() {
93        let expected = vec![
94            ColorChoice::Auto,
95            ColorChoice::AlwaysAnsi,
96            ColorChoice::Always,
97            ColorChoice::Never,
98        ];
99        let values: Vec<_> = expected
100            .iter()
101            .cloned()
102            .map(AtomicChoice::from_choice)
103            .collect();
104        let actual: Vec<_> = values
105            .iter()
106            .cloned()
107            .filter_map(AtomicChoice::to_choice)
108            .collect();
109        assert_eq!(expected, actual);
110    }
111}
112
113#[doc = include_str!("../README.md")]
114#[cfg(doctest)]
115pub struct ReadmeDoctests;