mdbook_plotly/code_handler/
color.rs1use plotly::color;
2use serde::{Deserialize, Deserializer, Serialize};
3use serde_json::Value;
4
5#[allow(clippy::enum_variant_names)]
6#[derive(Clone, Debug, Serialize)]
7#[serde(rename_all = "snake_case")]
8pub enum Color {
9 NamedColor(color::NamedColor),
10 RgbColor(color::Rgb),
11 RgbaColor(color::Rgba),
12}
13
14impl color::Color for Color {}
15
16impl<'de> Deserialize<'de> for Color {
17 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
18 where
19 D: Deserializer<'de>,
20 {
21 let value = Value::deserialize(deserializer)?;
22
23 if let Some(s) = value.as_str()
24 && let Ok(named) = serde_json::from_str::<color::NamedColor>(&format!("\"{s}\""))
25 {
26 return Ok(Self::NamedColor(named));
27 }
28
29 if let Some(s) = value.as_str()
30 && let Some(rgb) = parse_hex_color(s)
31 {
32 return Ok(Self::RgbColor(rgb));
33 }
34
35 if let Ok(rgb) = serde_json::from_value::<color::Rgb>(value.clone()) {
36 return Ok(Self::RgbColor(rgb));
37 }
38
39 if let Ok(rgba) = serde_json::from_value::<color::Rgba>(value) {
40 return Ok(Self::RgbaColor(rgba));
41 }
42
43 Err(serde::de::Error::custom("invalid color format"))
44 }
45}
46
47fn parse_hex_color(value: &str) -> Option<color::Rgb> {
48 let hex = value.strip_prefix('#')?;
49
50 let (r, g, b) = match hex.len() {
51 3 => {
52 let mut chars = hex.chars();
53 let r = chars.next()?;
54 let g = chars.next()?;
55 let b = chars.next()?;
56 let rr = u8::from_str_radix(&format!("{r}{r}"), 16).ok()?;
57 let gg = u8::from_str_radix(&format!("{g}{g}"), 16).ok()?;
58 let bb = u8::from_str_radix(&format!("{b}{b}"), 16).ok()?;
59 (rr, gg, bb)
60 }
61 6 => {
62 let r = u8::from_str_radix(&hex[0..2], 16).ok()?;
63 let g = u8::from_str_radix(&hex[2..4], 16).ok()?;
64 let b = u8::from_str_radix(&hex[4..6], 16).ok()?;
65 (r, g, b)
66 }
67 _ => return None,
68 };
69
70 Some(color::Rgb::new(r, g, b))
71}