mdbook_plotly/preprocessor/
config.rs1use log::warn;
2use serde::{Deserialize, Serialize, de::DeserializeOwned};
3use toml::{Value, value::Table};
4
5pub const SUPPORTED_MDBOOK_VERSION: &str = "0.5.2";
6pub const PREPROCESSOR_CONFIG_KEY: &str = "preprocessor.plotly";
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
10#[serde(default, rename_all = "kebab-case")]
11pub struct PreprocessorConfig {
12 pub output_type: PlotlyOutputType,
19
20 pub input_type: PlotlyInputType,
26
27 pub map_eval: MapEvalConfig,
30}
31
32impl PreprocessorConfig {
33 pub fn from_toml(value: &Value) -> Self {
34 let Some(table) = value.as_table() else {
35 warn!(
36 "Illegal config format for '{}': expected a table; using default configuration.",
37 PREPROCESSOR_CONFIG_KEY
38 );
39 return Self::default();
40 };
41
42 warn_unknown_keys(
43 PREPROCESSOR_CONFIG_KEY,
44 table,
45 &["output-type", "input-type", "map-eval"],
46 );
47
48 Self {
49 output_type: parse_field(
50 table,
51 "output-type",
52 &format!("{}.output-type", PREPROCESSOR_CONFIG_KEY),
53 Self::default().output_type,
54 ),
55 input_type: parse_field(
56 table,
57 "input-type",
58 &format!("{}.input-type", PREPROCESSOR_CONFIG_KEY),
59 Self::default().input_type,
60 ),
61 map_eval: match table.get("map-eval") {
62 Some(map_eval) => MapEvalConfig::from_toml(map_eval),
63 None => MapEvalConfig::default(),
64 },
65 }
66 }
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
70#[serde(default, rename_all = "kebab-case")]
71pub struct MapEvalConfig {
72 pub enabled: bool,
73 pub reuse_slab: bool,
74 pub compile_expressions: bool,
75 pub namespace_scope: MapNamespaceScope,
76}
77
78impl MapEvalConfig {
79 pub fn from_toml(value: &Value) -> Self {
80 let Some(table) = value.as_table() else {
81 warn!(
82 "Illegal config format for '{}.map-eval': expected a table; using default configuration.",
83 PREPROCESSOR_CONFIG_KEY
84 );
85 return Self::default();
86 };
87
88 warn_unknown_keys(
89 &format!("{}.map-eval", PREPROCESSOR_CONFIG_KEY),
90 table,
91 &[
92 "enabled",
93 "reuse-slab",
94 "compile-expressions",
95 "namespace-scope",
96 ],
97 );
98
99 Self {
100 enabled: parse_field(
101 table,
102 "enabled",
103 &format!("{}.map-eval.enabled", PREPROCESSOR_CONFIG_KEY),
104 Self::default().enabled,
105 ),
106 reuse_slab: parse_field(
107 table,
108 "reuse-slab",
109 &format!("{}.map-eval.reuse-slab", PREPROCESSOR_CONFIG_KEY),
110 Self::default().reuse_slab,
111 ),
112 compile_expressions: parse_field(
113 table,
114 "compile-expressions",
115 &format!("{}.map-eval.compile-expressions", PREPROCESSOR_CONFIG_KEY),
116 Self::default().compile_expressions,
117 ),
118 namespace_scope: parse_field(
119 table,
120 "namespace-scope",
121 &format!("{}.map-eval.namespace-scope", PREPROCESSOR_CONFIG_KEY),
122 Self::default().namespace_scope,
123 ),
124 }
125 }
126}
127
128fn parse_field<T>(table: &Table, key: &str, path: &str, default: T) -> T
129where
130 T: DeserializeOwned,
131{
132 match table.get(key) {
133 Some(value) => deserialize_value(value).unwrap_or_else(|e| {
134 warn!(
135 "Failed to parse config field '{}': {}; using default value.",
136 path, e
137 );
138 default
139 }),
140 None => default,
141 }
142}
143
144fn deserialize_value<T>(value: &Value) -> Result<T, toml::de::Error>
145where
146 T: DeserializeOwned,
147{
148 #[derive(Deserialize)]
149 struct Wrapper<T> {
150 value: T,
151 }
152
153 toml::from_str::<Wrapper<T>>(&format!("value = {}", value)).map(|wrapper| wrapper.value)
154}
155
156fn warn_unknown_keys(path: &str, table: &Table, known_keys: &[&str]) {
157 for key in table.keys() {
158 if !known_keys.contains(&key.as_str()) {
159 warn!("Unknown config key '{}.{}' will be ignored.", path, key);
160 }
161 }
162}
163
164impl Default for MapEvalConfig {
165 fn default() -> Self {
166 Self {
167 enabled: true,
168 reuse_slab: true,
169 compile_expressions: true,
170 namespace_scope: MapNamespaceScope::FullMap,
171 }
172 }
173}
174
175#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
176#[serde(rename_all = "kebab-case")]
177pub enum MapNamespaceScope {
178 #[default]
179 #[serde(rename = "full-map")]
180 FullMap,
181 #[serde(rename = "exports-only")]
182 ExportsOnly,
183}
184
185#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
187pub enum PlotlyOutputType {
188 #[default]
190 #[cfg(feature = "plotly-html-handler")]
191 #[serde(rename = "plotly-html")]
192 PlotlyHtml,
193
194 #[cfg(feature = "plotly-svg-handler")]
196 #[serde(rename = "plotly-svg")]
197 PlotlySvg,
198}
199
200#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
202pub enum PlotlyInputType {
203 #[default]
206 #[serde(rename = "json-input")]
207 JSONInput,
208
209 #[serde(rename = "toml-input")]
211 TOMLInput,
212}