Skip to main content

mdbook_plotly/code_handler/
color.rs

1use 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}