mdbook_plotly/preprocessor/handlers/code_handler/
until.rs1use 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-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 let mut namespace = fasteval::StrToF64Namespace::new();
146 let result = ez_eval(&condition, &mut namespace)?;
149 let selected = if result != 0.0 { true_val } else { false_val };
150
151 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 Self::parse_map(map, selected)
158 }
159 }
160 "time" => {
161 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 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#[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 {}