This repository has no description
1use std::{
2 collections::HashMap,
3 fs::File,
4 io::{BufRead, BufReader},
5 path::PathBuf,
6};
7
8use serde::Deserialize;
9use strum::IntoEnumIterator;
10use strum_macros::EnumIter;
11#[cfg(feature = "web")]
12use wasm_bindgen::prelude::*;
13
14#[cfg_attr(feature = "web", wasm_bindgen)]
15#[derive(Debug, Clone, Copy, PartialEq, EnumIter)]
16pub enum Color {
17 Black,
18 White,
19 Red,
20 Green,
21 Blue,
22 Yellow,
23 Orange,
24 Purple,
25 Brown,
26 Cyan,
27 Pink,
28 Gray,
29}
30
31impl Color {
32 pub fn all() -> Vec<Color> {
33 Color::iter().collect()
34 }
35}
36
37impl Default for Color {
38 fn default() -> Self {
39 Self::Black
40 }
41}
42
43impl From<&str> for Color {
44 fn from(s: &str) -> Self {
45 match s {
46 "black" => Color::Black,
47 "white" => Color::White,
48 "red" => Color::Red,
49 "green" => Color::Green,
50 "blue" => Color::Blue,
51 "yellow" => Color::Yellow,
52 "orange" => Color::Orange,
53 "purple" => Color::Purple,
54 "brown" => Color::Brown,
55 "cyan" => Color::Cyan,
56 "pink" => Color::Pink,
57 "gray" => Color::Gray,
58 _ => panic!("Invalid color: {}", s),
59 }
60 }
61}
62
63impl Color {
64 pub fn render(self, mapping: &ColorMapping) -> String {
65 match self {
66 Color::Black => mapping.black.to_string(),
67 Color::White => mapping.white.to_string(),
68 Color::Red => mapping.red.to_string(),
69 Color::Green => mapping.green.to_string(),
70 Color::Blue => mapping.blue.to_string(),
71 Color::Yellow => mapping.yellow.to_string(),
72 Color::Orange => mapping.orange.to_string(),
73 Color::Purple => mapping.purple.to_string(),
74 Color::Brown => mapping.brown.to_string(),
75 Color::Cyan => mapping.cyan.to_string(),
76 Color::Pink => mapping.pink.to_string(),
77 Color::Gray => mapping.gray.to_string(),
78 }
79 }
80
81 pub fn name(&self) -> String {
82 match self {
83 Color::Black => "black",
84 Color::White => "white",
85 Color::Red => "red",
86 Color::Green => "green",
87 Color::Blue => "blue",
88 Color::Yellow => "yellow",
89 Color::Orange => "orange",
90 Color::Purple => "purple",
91 Color::Brown => "brown",
92 Color::Cyan => "cyan",
93 Color::Pink => "pink",
94 Color::Gray => "gray",
95 }
96 .to_string()
97 }
98}
99
100#[cfg_attr(feature = "web", wasm_bindgen(getter_with_clone))]
101#[derive(Debug, Deserialize, Clone)]
102pub struct ColorMapping {
103 pub black: String,
104 pub white: String,
105 pub red: String,
106 pub green: String,
107 pub blue: String,
108 pub yellow: String,
109 pub orange: String,
110 pub purple: String,
111 pub brown: String,
112 pub cyan: String,
113 pub pink: String,
114 pub gray: String,
115}
116
117#[cfg_attr(feature = "web", wasm_bindgen)]
118impl ColorMapping {
119 // wasm_bindegen is not supported on trait impls
120 #[allow(clippy::should_implement_trait)]
121 pub fn default() -> Self {
122 ColorMapping {
123 black: "black".to_string(),
124 white: "white".to_string(),
125 red: "red".to_string(),
126 green: "green".to_string(),
127 blue: "blue".to_string(),
128 yellow: "yellow".to_string(),
129 orange: "orange".to_string(),
130 purple: "purple".to_string(),
131 brown: "brown".to_string(),
132 pink: "pink".to_string(),
133 gray: "gray".to_string(),
134 cyan: "cyan".to_string(),
135 }
136 }
137
138 pub fn from_json(content: &str) -> ColorMapping {
139 let json: HashMap<String, String> =
140 serde_json::from_str(content).unwrap();
141 ColorMapping::from_hashmap(json)
142 }
143
144 pub fn from_css(content: &str) -> ColorMapping {
145 let mut mapping = ColorMapping::default();
146 for line in content.lines() {
147 mapping.from_css_line(line);
148 }
149 mapping
150 }
151}
152
153#[cfg(not(feature = "web"))]
154impl Default for ColorMapping {
155 fn default() -> Self {
156 ColorMapping::default()
157 }
158}
159
160impl ColorMapping {
161 pub fn from_cli_args(args: &Vec<String>) -> ColorMapping {
162 let mut colormap: HashMap<String, String> = HashMap::new();
163 for mapping in args {
164 if !mapping.contains(':') {
165 println!("Invalid color mapping: {}", mapping);
166 std::process::exit(1);
167 }
168 let mut split = mapping.split(':');
169 let color = split.next().unwrap();
170 let hex = split.next().unwrap();
171 colormap.insert(color.to_string(), hex.to_string());
172 }
173 ColorMapping::from_hashmap(colormap)
174 }
175
176 pub fn from_hashmap(hashmap: HashMap<String, String>) -> ColorMapping {
177 ColorMapping {
178 black: hashmap
179 .get("black")
180 .unwrap_or(&ColorMapping::default().black)
181 .to_string(),
182 white: hashmap
183 .get("white")
184 .unwrap_or(&ColorMapping::default().white)
185 .to_string(),
186 red: hashmap
187 .get("red")
188 .unwrap_or(&ColorMapping::default().red)
189 .to_string(),
190 green: hashmap
191 .get("green")
192 .unwrap_or(&ColorMapping::default().green)
193 .to_string(),
194 blue: hashmap
195 .get("blue")
196 .unwrap_or(&ColorMapping::default().blue)
197 .to_string(),
198 yellow: hashmap
199 .get("yellow")
200 .unwrap_or(&ColorMapping::default().yellow)
201 .to_string(),
202 orange: hashmap
203 .get("orange")
204 .unwrap_or(&ColorMapping::default().orange)
205 .to_string(),
206 purple: hashmap
207 .get("purple")
208 .unwrap_or(&ColorMapping::default().purple)
209 .to_string(),
210 brown: hashmap
211 .get("brown")
212 .unwrap_or(&ColorMapping::default().brown)
213 .to_string(),
214 cyan: hashmap
215 .get("cyan")
216 .unwrap_or(&ColorMapping::default().cyan)
217 .to_string(),
218 pink: hashmap
219 .get("pink")
220 .unwrap_or(&ColorMapping::default().pink)
221 .to_string(),
222 gray: hashmap
223 .get("gray")
224 .unwrap_or(&ColorMapping::default().gray)
225 .to_string(),
226 }
227 }
228
229 pub fn from_file(path: PathBuf) -> ColorMapping {
230 match path.extension().map(|e| e.to_str().unwrap()) {
231 Some("css") => ColorMapping::from_css_file(path),
232 Some("json") => ColorMapping::from_json_file(path),
233 ext => panic!(
234 "Invalid colormap file format. Must be css or json, is {:?}.",
235 ext
236 ),
237 }
238 }
239
240 pub fn from_json_file(path: PathBuf) -> ColorMapping {
241 let file = File::open(path).unwrap();
242 let reader = BufReader::new(file);
243 let json: HashMap<String, String> =
244 serde_json::from_reader(reader).unwrap();
245 ColorMapping::from_hashmap(json)
246 }
247
248 pub fn from_css_file(path: PathBuf) -> ColorMapping {
249 let mut mapping = ColorMapping::default();
250 let file = File::open(path).unwrap();
251 let lines = std::io::BufReader::new(file).lines();
252 for line in lines.map_while(Result::ok) {
253 mapping.from_css_line(&line);
254 }
255 mapping
256 }
257
258 #[allow(clippy::wrong_self_convention)]
259 fn from_css_line(&mut self, line: &str) {
260 if let Some((name, value)) = line.trim().split_once(':') {
261 let value = value.trim().trim_end_matches(';').to_owned();
262 match name.trim() {
263 "black" => self.black = value,
264 "white" => self.white = value,
265 "red" => self.red = value,
266 "green" => self.green = value,
267 "blue" => self.blue = value,
268 "yellow" => self.yellow = value,
269 "orange" => self.orange = value,
270 "purple" => self.purple = value,
271 "brown" => self.brown = value,
272 "cyan" => self.cyan = value,
273 "pink" => self.pink = value,
274 "gray" => self.gray = value,
275 _ => (),
276 }
277 }
278 }
279}