Skip to main content

mdbook_plotly/preprocessor/handlers/code_handler/
until.rs

1use anyhow::{Context, 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())
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()).with_context(|| {
64                    format!(
65                        "failed to deserialize generated list for type '{}'",
66                        value_type
67                    )
68                })
69            }
70            "g-number" => {
71                let expr: String = must_translate(&mut value, map, "expr")?;
72                let data = ez_eval(&expr, &mut fasteval::EmptyNamespace {})?;
73                serde_json::from_value(data.into()).with_context(|| {
74                    format!(
75                        "failed to deserialize generated number for type '{}'",
76                        value_type
77                    )
78                })
79            }
80            "g-range" => {
81                let begin: f64 = must_translate(&mut value, map, "begin")?;
82                let end: f64 = must_translate(&mut value, map, "end")?;
83                let step: f64 = if value.get("step").is_some() {
84                    must_translate(&mut value, map, "step")?
85                } else {
86                    1.0
87                };
88                if step <= 0.0 {
89                    return Err(anyhow!("step must be positive"));
90                }
91                let mut result = vec![];
92                let mut current = begin;
93                while current < end {
94                    result.push(Value::from(current));
95                    current += step;
96                }
97                serde_json::from_value(result.into()).with_context(|| {
98                    format!(
99                        "failed to deserialize generated range for type '{}'",
100                        value_type
101                    )
102                })
103            }
104            "g-repeat" => {
105                let val: Value = must_translate(&mut value, map, "value")?;
106                let count: u64 = must_translate(&mut value, map, "count")?;
107                let result = std::iter::repeat_n(val, count as usize).collect::<Vec<_>>();
108                serde_json::from_value(result.into()).with_context(|| {
109                    format!(
110                        "failed to deserialize repeated values for type '{}'",
111                        value_type
112                    )
113                })
114            }
115            "g-linear" => {
116                let begin: f64 = must_translate(&mut value, map, "begin")?;
117                let end: f64 = must_translate(&mut value, map, "end")?;
118                let count: u64 = must_translate(&mut value, map, "count")?;
119                if count == 0 {
120                    return Err(anyhow!("count must be positive"));
121                }
122                let mut result = Vec::with_capacity(count as usize);
123                if count == 1 {
124                    result.push(Value::from(begin));
125                } else {
126                    let step = (end - begin) / ((count - 1) as f64);
127                    for i in 0..count {
128                        let val = begin + (i as f64) * step;
129                        result.push(Value::from(val));
130                    }
131                }
132                serde_json::from_value(result.into()).with_context(|| {
133                    format!(
134                        "failed to deserialize linear spaced values for type '{}'",
135                        value_type
136                    )
137                })
138            }
139            "if" => {
140                let condition: String = must_translate(&mut value, map, "condition")?;
141                let true_val: Value = must_translate(&mut value, map, "true")?;
142                let false_val: Value = must_translate(&mut value, map, "false")?;
143
144                // Evaluate condition using fasteval
145                let mut namespace = fasteval::StrToF64Namespace::new();
146                // We need to check if the condition contains 'i' variable
147                // For simplicity, assume it's a boolean expression that evaluates to non-zero for true
148                let result = ez_eval(&condition, &mut namespace)?;
149                let selected = if result != 0.0 { true_val } else { false_val };
150
151                // Parse the selected value according to its type
152                if !selected.is_object() {
153                    serde_json::from_value::<T>(selected.clone())
154                        .with_context(|| "failed to deserialize non-object if result")
155                } else {
156                    // If it's an object, it might be another generator
157                    Self::parse_map(map, selected)
158                }
159            }
160            "time" => {
161                // Simple time generator: produce timestamps as strings
162                let start: String = must_translate(&mut value, map, "start")?;
163                let end: String = must_translate(&mut value, map, "end")?;
164                let _interval: String = must_translate(&mut value, map, "interval")?;
165                let _format: Option<String> = if value.get("format").is_some() {
166                    Some(must_translate(&mut value, map, "format")?)
167                } else {
168                    None
169                };
170
171                // For now, just return the start and end as strings
172                // In a real implementation, you'd parse dates and generate a series
173                let result = vec![Value::from(start.clone()), Value::from(end.clone())];
174                serde_json::from_value(result.into()).with_context(|| {
175                    format!(
176                        "failed to deserialize time values for type '{}'",
177                        value_type
178                    )
179                })
180            }
181            _ => Err(anyhow!("unknown type `{}`", value_type)),
182        }
183    }
184
185    pub fn unwrap(self, map: &Map) -> Result<T> {
186        let result = match self {
187            Self::Data(data) => data,
188            Self::Index(index) => {
189                let value = map.get(&index).ok_or_else(|| {
190                    let available_keys: Vec<_> = map.keys().take(5).cloned().collect();
191                    anyhow!(
192                        "Invalid index: '{}' (available keys: {}{})",
193                        &index,
194                        available_keys.join(", "),
195                        if map.keys().len() > 5 { ", ..." } else { "" }
196                    )
197                })?;
198                Self::parse_map(map, value.clone())
199                    .with_context(|| format!("failed to parse map entry '{}'", index))?
200            }
201        };
202        Ok(result)
203    }
204}
205
206impl<'de, T> Deserialize<'de> for DataPack<T>
207where
208    T: DeserializeOwned + Serialize + Debug + Clone,
209{
210    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
211    where
212        D: Deserializer<'de>,
213    {
214        let value = Value::deserialize(deserializer)?;
215        if let Value::String(ref s) = value
216            && let Some(idx) = s.strip_prefix("map.")
217        {
218            return Ok(DataPack::Index(idx.to_string()));
219        }
220        serde_json::from_value::<T>(value)
221            .map(DataPack::Data)
222            .map_err(serde::de::Error::custom)
223    }
224}
225
226impl<T> Serialize for DataPack<T>
227where
228    T: DeserializeOwned + Serialize + Debug + Clone,
229{
230    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231    where
232        S: Serializer,
233    {
234        match self {
235            Self::Data(data) => data.serialize(serializer),
236            Self::Index(index) => serializer.serialize_str(&format!("map.{index}")),
237        }
238    }
239}
240
241use plotly::color;
242
243// This is to make Json look clearer when it is written.
244#[allow(clippy::enum_variant_names)]
245#[derive(Clone, Debug, Serialize, Deserialize)]
246#[serde(rename_all = "snake_case")]
247pub enum Color {
248    NamedColor(color::NamedColor),
249    RgbColor(color::Rgb),
250    RgbaColor(color::Rgba),
251}
252
253impl color::Color for Color {}