Skip to main content

mdbook_plotly/preprocessor/handlers/code_handler/
until.rs

1use anyhow::{Result, anyhow, Context};
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())
18        .with_context(|| format!("failed to deserialize field '{}'", name))?
19        .unwrap(map)
20        .with_context(|| format!("failed to unwrap DataPack for field '{}'", name))?;
21    Ok(result)
22}
23
24#[derive(Clone, Debug)]
25pub enum DataPack<T> {
26    Data(T),
27    Index(String),
28}
29
30impl<T> DataPack<T>
31where
32    T: DeserializeOwned + Serialize + Debug + Clone,
33{
34    fn parse_map(map: &Map, mut value: Value) -> Result<T> {
35        if !value.is_object() {
36            let direct_result = serde_json::from_value::<T>(value.clone())
37                .with_context(|| "failed to deserialize non-object value")?;
38            return Ok(direct_result);
39        }
40        let value_type = if let Some(Value::String(s)) = value.get("type") {
41            s.clone()
42        } else {
43            return Err(anyhow!("`type` must be a string"));
44        };
45        use fasteval::ez_eval;
46        match value_type.as_str() {
47            "raw" => {
48                let result = must_translate(&mut value, map, "data")?;
49                Ok(result)
50            }
51            // `g-` means generator
52            "g-number-list" => {
53                let index_begin: u64 = must_translate(&mut value, map, "begin")?;
54                let index_end: u64 = must_translate(&mut value, map, "end")?;
55                let expr: String = must_translate(&mut value, map, "expr")?;
56                let mut result = vec![];
57                let mut namespace = fasteval::StrToF64Namespace::new();
58                for i in index_begin..index_end {
59                    namespace.insert("i", i as f64);
60                    let data = ez_eval(&expr, &mut namespace)?;
61                    result.push(Value::from(data));
62                }
63                serde_json::from_value(result.into())
64                    .with_context(|| format!("failed to deserialize generated list for type '{}'", value_type))
65            }
66            "g-number" => {
67                let expr: String = must_translate(&mut value, map, "expr")?;
68                let data = ez_eval(&expr, &mut fasteval::EmptyNamespace {})?;
69                serde_json::from_value(data.into())
70                    .with_context(|| format!("failed to deserialize generated number for type '{}'", value_type))
71            }
72            "g-range" => {
73                let begin: f64 = must_translate(&mut value, map, "begin")?;
74                let end: f64 = must_translate(&mut value, map, "end")?;
75                let step: f64 = if value.get("step").is_some() {
76                    must_translate(&mut value, map, "step")?
77                } else {
78                    1.0
79                };
80                if step <= 0.0 {
81                    return Err(anyhow!("step must be positive"));
82                }
83                let mut result = vec![];
84                let mut current = begin;
85                while current < end {
86                    result.push(Value::from(current));
87                    current += step;
88                }
89                serde_json::from_value(result.into())
90                    .with_context(|| format!("failed to deserialize generated range for type '{}'", value_type))
91            }
92            "g-repeat" => {
93                let val: Value = must_translate(&mut value, map, "value")?;
94                let count: u64 = must_translate(&mut value, map, "count")?;
95                let result = std::iter::repeat_n(val, count as usize).collect::<Vec<_>>();
96                serde_json::from_value(result.into())
97                    .with_context(|| format!("failed to deserialize repeated values for type '{}'", value_type))
98            }
99            "g-linear" => {
100                let begin: f64 = must_translate(&mut value, map, "begin")?;
101                let end: f64 = must_translate(&mut value, map, "end")?;
102                let count: u64 = must_translate(&mut value, map, "count")?;
103                if count == 0 {
104                    return Err(anyhow!("count must be positive"));
105                }
106                let mut result = Vec::with_capacity(count as usize);
107                if count == 1 {
108                    result.push(Value::from(begin));
109                } else {
110                    let step = (end - begin) / ((count - 1) as f64);
111                    for i in 0..count {
112                        let val = begin + (i as f64) * step;
113                        result.push(Value::from(val));
114                    }
115                }
116                serde_json::from_value(result.into())
117                    .with_context(|| format!("failed to deserialize linear spaced values for type '{}'", value_type))
118            }
119            _ => Err(anyhow!("unknown type `{}`", value_type)),
120        }
121    }
122
123    pub fn unwrap(self, map: &Map) -> Result<T> {
124        let result = match self {
125            Self::Data(data) => data,
126            Self::Index(index) => {
127                let value = map.get(&index).ok_or_else(|| {
128                    let available_keys: Vec<_> = map.keys().take(5).cloned().collect();
129                    anyhow!(
130                        "Invalid index: '{}' (available keys: {}{})",
131                        &index,
132                        available_keys.join(", "),
133                        if map.keys().len() > 5 { ", ..." } else { "" }
134                    )
135                })?;
136                Self::parse_map(map, value.clone())
137                    .with_context(|| format!("failed to parse map entry '{}'", index))?
138            }
139        };
140        Ok(result)
141    }
142}
143
144impl<'de, T> Deserialize<'de> for DataPack<T>
145where
146    T: DeserializeOwned + Serialize + Debug + Clone,
147{
148    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
149    where
150        D: Deserializer<'de>,
151    {
152        let value = Value::deserialize(deserializer)?;
153        if let Value::String(ref s) = value
154            && let Some(idx) = s.strip_prefix("map.")
155        {
156            return Ok(DataPack::Index(idx.to_string()));
157        }
158        serde_json::from_value::<T>(value)
159            .map(DataPack::Data)
160            .map_err(serde::de::Error::custom)
161    }
162}
163
164impl<T> Serialize for DataPack<T>
165where
166    T: DeserializeOwned + Serialize + Debug + Clone,
167{
168    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169    where
170        S: Serializer,
171    {
172        match self {
173            Self::Data(data) => data.serialize(serializer),
174            Self::Index(index) => serializer.serialize_str(&format!("map.{index}")),
175        }
176    }
177}
178
179use plotly::color;
180
181// This is to make Json look clearer when it is written.
182#[allow(clippy::enum_variant_names)]
183#[derive(Clone, Debug, Serialize, Deserialize)]
184#[serde(rename_all = "snake_case")]
185pub enum Color {
186    NamedColor(color::NamedColor),
187    RgbColor(color::Rgb),
188    RgbaColor(color::Rgba),
189}
190
191impl color::Color for Color {}