Skip to main content

mdbook_plotly/preprocessor/handlers/code_handler/
until.rs

1use anyhow::{Result, anyhow};
2use serde::{Deserialize, Deserializer, Serialize, Serializer, de::DeserializeOwned};
3use serde_json::{Map as JsonMap, Value, value::Index};
4use std::fmt::{Debug, Display};
5
6pub type Map = JsonMap<String, Value>;
7
8#[inline]
9pub fn must_translate<T, N>(obj: &mut Value, map: &Map, name: N) -> Result<T>
10where
11    T: DeserializeOwned + Serialize + Debug + Clone,
12    N: Index + Display,
13{
14    let result = obj
15        .get_mut(&name)
16        .ok_or(anyhow!("missing `{}` field", name))?;
17    let result = serde_json::from_value::<DataPack<T>>(result.take())?.unwrap(map)?;
18    Ok(result)
19}
20
21#[derive(Clone, Debug)]
22pub enum DataPack<T> {
23    Data(T),
24    Index(String),
25}
26
27impl<T> DataPack<T>
28where
29    T: DeserializeOwned + Serialize + Debug + Clone,
30{
31    fn parse_map(map: &Map, mut value: Value) -> Result<T> {
32        if !value.is_object() {
33            let direct_result = serde_json::from_value::<T>(value.clone())?;
34            return Ok(direct_result);
35        }
36        let value_type = &value["type"];
37        let value_type = if !value_type.is_string() {
38            return Err(anyhow!("`type` must be a string"));
39        } else {
40            // SAFETY: `value_type` is a string
41            value_type.as_str().unwrap()
42        };
43        use fasteval::ez_eval;
44        match value_type {
45            "raw" => {
46                let result = must_translate(&mut value, map, "data")?;
47                Ok(result)
48            }
49            // `g-` means generator
50            "g-number-list" => {
51                let index_begin: u64 = must_translate(&mut value, map, "begin")?;
52                let index_end: u64 = must_translate(&mut value, map, "end")?;
53                let expr: String = must_translate(&mut value, map, "expr")?;
54                let mut result = vec![];
55                let mut namespace = fasteval::StrToF64Namespace::new();
56                for i in index_begin..index_end {
57                    namespace.insert("i", i as f64);
58                    let data = ez_eval(&expr, &mut namespace)?;
59                    result.push(Value::from(data));
60                }
61                Ok(serde_json::from_value(result.into())?)
62            }
63            "g-number" => {
64                let expr: String = must_translate(&mut value, map, "expr")?;
65                let data = ez_eval(&expr, &mut fasteval::EmptyNamespace {})?;
66                Ok(serde_json::from_value(data.into())?)
67            }
68            _ => Err(anyhow!("unknown type `{}`", value_type)),
69        }
70    }
71
72    pub fn unwrap(self, map: &Map) -> Result<T> {
73        let result = match self {
74            Self::Data(data) => data,
75            Self::Index(index) => {
76                let value = map
77                    .get(&index)
78                    .ok_or_else(|| anyhow!("Invalid index: {}", &index))?;
79                Self::parse_map(map, value.clone())?
80            }
81        };
82        Ok(result)
83    }
84}
85
86impl<'de, T> Deserialize<'de> for DataPack<T>
87where
88    T: DeserializeOwned + Serialize + Debug + Clone,
89{
90    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91    where
92        D: Deserializer<'de>,
93    {
94        let value = Value::deserialize(deserializer)?;
95        if let Value::String(ref s) = value
96            && let Some(idx) = s.strip_prefix("map.")
97        {
98            return Ok(DataPack::Index(idx.to_string()));
99        }
100        serde_json::from_value::<T>(value)
101            .map(DataPack::Data)
102            .map_err(serde::de::Error::custom)
103    }
104}
105
106impl<T> Serialize for DataPack<T>
107where
108    T: DeserializeOwned + Serialize + Debug + Clone,
109{
110    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111    where
112        S: Serializer,
113    {
114        match self {
115            Self::Data(data) => data.serialize(serializer),
116            Self::Index(index) => serializer.serialize_str(&format!("map.{index}")),
117        }
118    }
119}
120
121use plotly::color;
122
123// This is to make Json look clearer when it is written.
124#[allow(clippy::enum_variant_names)]
125#[derive(Clone, Debug, Serialize, Deserialize)]
126#[serde(rename_all = "snake_case")]
127pub enum Color {
128    NamedColor(color::NamedColor),
129    RgbColor(color::Rgb),
130    RgbaColor(color::Rgba),
131}
132
133impl color::Color for Color {}