colorchoice/
lib.rs

1//! Global override of color control
2
3#![cfg_attr(not(test), no_std)]
4
5use core::sync::atomic::{AtomicUsize, Ordering};
6
7/// Selection for overriding color output
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
9pub enum ColorChoice {
10    Auto,
11    AlwaysAnsi,
12    Always,
13    Never,
14}
15
16impl ColorChoice {
17    /// Get the current [`ColorChoice`] state
18    pub fn global() -> Self {
19        USER.get()
20    }
21
22    /// Override the detected [`ColorChoice`]
23    pub fn write_global(self) {
24        USER.set(self)
25    }
26}
27
28impl Default for ColorChoice {
29    fn default() -> Self {
30        Self::Auto
31    }
32}
33
34static USER: AtomicChoice = AtomicChoice::new();
35
36#[derive(Debug)]
37pub(crate) struct AtomicChoice(AtomicUsize);
38
39impl AtomicChoice {
40    pub(crate) const fn new() -> Self {
41        Self(AtomicUsize::new(Self::from_choice(
42            crate::ColorChoice::Auto,
43        )))
44    }
45
46    pub(crate) fn get(&self) -> crate::ColorChoice {
47        let choice = self.0.load(Ordering::SeqCst);
48        Self::to_choice(choice).unwrap()
49    }
50
51    pub(crate) fn set(&self, choice: crate::ColorChoice) {
52        let choice = Self::from_choice(choice);
53        self.0.store(choice, Ordering::SeqCst);
54    }
55
56    const fn from_choice(choice: crate::ColorChoice) -> usize {
57        match choice {
58            crate::ColorChoice::Auto => 0,
59            crate::ColorChoice::AlwaysAnsi => 1,
60            crate::ColorChoice::Always => 2,
61            crate::ColorChoice::Never => 3,
62        }
63    }
64
65    const fn to_choice(choice: usize) -> Option<crate::ColorChoice> {
66        match choice {
67            0 => Some(crate::ColorChoice::Auto),
68            1 => Some(crate::ColorChoice::AlwaysAnsi),
69            2 => Some(crate::ColorChoice::Always),
70            3 => Some(crate::ColorChoice::Never),
71            _ => None,
72        }
73    }
74}
75
76impl Default for AtomicChoice {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82#[cfg(test)]
83mod test {
84    use super::*;
85
86    #[test]
87    fn choice_serialization() {
88        let expected = vec![
89            ColorChoice::Auto,
90            ColorChoice::AlwaysAnsi,
91            ColorChoice::Always,
92            ColorChoice::Never,
93        ];
94        let values: Vec<_> = expected
95            .iter()
96            .cloned()
97            .map(AtomicChoice::from_choice)
98            .collect();
99        let actual: Vec<_> = values
100            .iter()
101            .cloned()
102            .filter_map(AtomicChoice::to_choice)
103            .collect();
104        assert_eq!(expected, actual);
105    }
106}