Skip to main content

mdbook_plotly/code_handler/
map.rs

1use crate::preprocessor::config::MapNamespaceScope;
2use fasteval::EvalNamespace;
3use serde_json::{Map as JsonMap, Value};
4use std::collections::BTreeMap;
5
6pub type Map = JsonMap<String, Value>;
7pub(crate) type Vars = BTreeMap<String, f64>;
8
9pub(crate) fn lookup_path<'a>(map: &'a Map, name: &str) -> Option<&'a Value> {
10    let path = name.strip_prefix("map.").unwrap_or(name);
11    let mut parts = path.split('.');
12    let first = parts.next()?;
13    let mut value = map.get(first)?;
14
15    for part in parts {
16        match value {
17            Value::Object(obj) => value = obj.get(part)?,
18            Value::Array(arr) => {
19                let idx = part.parse::<usize>().ok()?;
20                value = arr.get(idx)?;
21            }
22            _ => return None,
23        }
24    }
25
26    Some(value)
27}
28
29pub(crate) fn map_value<'a>(map: &'a Map, index: &str) -> anyhow::Result<&'a Value> {
30    lookup_path(map, index).ok_or_else(|| anyhow::anyhow!("missing map value `{}`", index))
31}
32
33fn value_to_f64(value: &Value) -> Option<f64> {
34    match value {
35        Value::Number(n) => n.as_f64(),
36        Value::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
37        Value::String(s) => s.parse::<f64>().ok(),
38        _ => None,
39    }
40}
41
42pub(crate) struct MapNamespace<'a> {
43    map: &'a Map,
44    vars: &'a Vars,
45    scope: &'a MapNamespaceScope,
46}
47
48impl<'a> MapNamespace<'a> {
49    pub(crate) fn new(map: &'a Map, vars: &'a Vars, scope: &'a MapNamespaceScope) -> Self {
50        Self { map, vars, scope }
51    }
52
53    fn lookup_map_value(&self, name: &str) -> Option<f64> {
54        match self.scope {
55            MapNamespaceScope::FullMap => lookup_path(self.map, name).and_then(value_to_f64),
56            MapNamespaceScope::ExportsOnly => {
57                let path = if name.starts_with("map.exports.") {
58                    name.to_owned()
59                } else {
60                    format!("exports.{name}")
61                };
62                lookup_path(self.map, &path).and_then(value_to_f64)
63            }
64        }
65    }
66}
67
68impl EvalNamespace for MapNamespace<'_> {
69    fn lookup(&mut self, name: &str, _args: Vec<f64>, _keybuf: &mut String) -> Option<f64> {
70        self.vars
71            .get(name)
72            .copied()
73            .or_else(|| self.lookup_map_value(name))
74    }
75}