Skip to main content

vihaco_module/
color.rs

1// SPDX-FileCopyrightText: 2026 The vihaco Authors
2// SPDX-License-Identifier: MIT
3
4use colored::{Color::TrueColor, Colorize};
5use std::fmt::{Display, Formatter, Result as FmtResult};
6
7pub struct Decorated<'a, T: Display> {
8    data: &'a T,
9    prefix: Option<String>,
10    color: Option<colored::Color>,
11}
12
13impl<'a, T: Display> Decorated<'a, T> {
14    pub fn new(data: &'a T) -> Self {
15        Decorated {
16            data,
17            prefix: None,
18            color: None,
19        }
20    }
21
22    pub fn true_color(&mut self, r: u8, g: u8, b: u8) -> Self {
23        Decorated {
24            data: self.data,
25            prefix: self.prefix.clone(),
26            color: Some(TrueColor { r, g, b }),
27        }
28    }
29}
30
31impl<'a, T: Display> Display for Decorated<'a, T> {
32    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
33        let prefix = self.prefix.clone().unwrap_or_default();
34
35        if f.alternate() {
36            let formatted = format!("{}{:#}", prefix, self.data);
37            if let Some(color) = self.color {
38                write!(f, "{}", formatted.color(color))
39            } else {
40                write!(f, "{}", formatted)
41            }
42        } else {
43            write!(f, "{}", prefix)?;
44            self.data.fmt(f)
45        }
46    }
47}
48
49pub trait Themed: Display + Sized {
50    fn ty(&self) -> Decorated<'_, Self> {
51        Decorated::new(self).true_color(102, 217, 239)
52    }
53    fn target(&self) -> Decorated<'_, Self> {
54        Decorated::new(self).true_color(174, 129, 255)
55    }
56    fn keyword(&self) -> Decorated<'_, Self> {
57        Decorated::new(self).true_color(249, 38, 114)
58    }
59    fn symbol(&self) -> Decorated<'_, Self> {
60        Decorated::new(self).true_color(166, 226, 46)
61    }
62    fn at_symbol(&self) -> Decorated<'_, Self> {
63        let mut decorated = self.symbol();
64        decorated.prefix = Some("@".to_string());
65        decorated
66    }
67
68    fn comment(&self) -> Decorated<'_, Self> {
69        Decorated::new(self).true_color(117, 113, 94)
70    }
71
72    fn instruction(&self) -> Decorated<'_, Self> {
73        Decorated::new(self).true_color(102, 217, 239)
74    }
75}
76
77impl<T: Display> Themed for T {}
78
79impl<'a, T: Display> From<&'a T> for Decorated<'a, T> {
80    fn from(value: &'a T) -> Self {
81        Decorated::new(value)
82    }
83}
84
85impl<'a, T: Display> From<&'a mut T> for Decorated<'a, T> {
86    fn from(value: &'a mut T) -> Self {
87        Decorated::new(value)
88    }
89}
90
91impl<T: Display> From<T> for Decorated<'_, T> {
92    fn from(value: T) -> Self {
93        Decorated::new(Box::leak(Box::new(value)))
94    }
95}
96
97#[macro_export]
98macro_rules! show {
99    ($fmt:expr $(, $arg:expr)* $(,)?) => {
100        {
101            $($arg.fmt($fmt)?;)*
102        }
103    };
104}
105
106#[macro_export]
107macro_rules! show_instruction {
108    ($fmt:expr, $inst:expr $(, $arg:expr)* $(,)?) => {
109        {
110            $inst.instruction().fmt($fmt)?;
111            $($arg.fmt($fmt)?;)*
112        }
113    };
114}
115
116pub use show;
117pub use show_instruction;
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn test_decorated() {
125        let value = 42;
126        let decorated = Decorated::new(&value).true_color(255, 0, 0);
127        println!("{}", decorated);
128        println!("{:#}", decorated);
129    }
130
131    #[test]
132    fn test_themed() {
133        let value = "test";
134        println!("{:#}", value.ty());
135        println!("{:#}", value.target());
136        println!("{:#}", value.keyword());
137        println!("{:#}", value.symbol());
138        println!("{:#}", value.at_symbol());
139        println!("{:#}", value.comment());
140        println!("{:#}", value.instruction());
141    }
142}