use log::warn;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use toml::{Value, value::Table};
pub const SUPPORTED_MDBOOK_VERSION: &str = "0.5.2";
pub const PREPROCESSOR_CONFIG_KEY: &str = "preprocessor.plotly";
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default, rename_all = "kebab-case")]
pub struct PreprocessorConfig {
pub output_type: PlotlyOutputType,
pub input_type: PlotlyInputType,
pub map_eval: MapEvalConfig,
}
impl PreprocessorConfig {
pub fn from_toml(value: &Value) -> Self {
let Some(table) = value.as_table() else {
warn!(
"Illegal config format for '{}': expected a table; using default configuration.",
PREPROCESSOR_CONFIG_KEY
);
return Self::default();
};
warn_unknown_keys(
PREPROCESSOR_CONFIG_KEY,
table,
&["output-type", "input-type", "map-eval"],
);
Self {
output_type: parse_field(
table,
"output-type",
&format!("{}.output-type", PREPROCESSOR_CONFIG_KEY),
Self::default().output_type,
),
input_type: parse_field(
table,
"input-type",
&format!("{}.input-type", PREPROCESSOR_CONFIG_KEY),
Self::default().input_type,
),
map_eval: match table.get("map-eval") {
Some(map_eval) => MapEvalConfig::from_toml(map_eval),
None => MapEvalConfig::default(),
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default, rename_all = "kebab-case")]
pub struct MapEvalConfig {
pub enabled: bool,
pub reuse_slab: bool,
pub compile_expressions: bool,
pub namespace_scope: MapNamespaceScope,
}
impl MapEvalConfig {
pub fn from_toml(value: &Value) -> Self {
let Some(table) = value.as_table() else {
warn!(
"Illegal config format for '{}.map-eval': expected a table; using default configuration.",
PREPROCESSOR_CONFIG_KEY
);
return Self::default();
};
warn_unknown_keys(
&format!("{}.map-eval", PREPROCESSOR_CONFIG_KEY),
table,
&[
"enabled",
"reuse-slab",
"compile-expressions",
"namespace-scope",
],
);
Self {
enabled: parse_field(
table,
"enabled",
&format!("{}.map-eval.enabled", PREPROCESSOR_CONFIG_KEY),
Self::default().enabled,
),
reuse_slab: parse_field(
table,
"reuse-slab",
&format!("{}.map-eval.reuse-slab", PREPROCESSOR_CONFIG_KEY),
Self::default().reuse_slab,
),
compile_expressions: parse_field(
table,
"compile-expressions",
&format!("{}.map-eval.compile-expressions", PREPROCESSOR_CONFIG_KEY),
Self::default().compile_expressions,
),
namespace_scope: parse_field(
table,
"namespace-scope",
&format!("{}.map-eval.namespace-scope", PREPROCESSOR_CONFIG_KEY),
Self::default().namespace_scope,
),
}
}
}
fn parse_field<T>(table: &Table, key: &str, path: &str, default: T) -> T
where
T: DeserializeOwned,
{
match table.get(key) {
Some(value) => deserialize_value(value).unwrap_or_else(|e| {
warn!(
"Failed to parse config field '{}': {}; using default value.",
path, e
);
default
}),
None => default,
}
}
fn deserialize_value<T>(value: &Value) -> Result<T, toml::de::Error>
where
T: DeserializeOwned,
{
#[derive(Deserialize)]
struct Wrapper<T> {
value: T,
}
toml::from_str::<Wrapper<T>>(&format!("value = {}", value)).map(|wrapper| wrapper.value)
}
fn warn_unknown_keys(path: &str, table: &Table, known_keys: &[&str]) {
for key in table.keys() {
if !known_keys.contains(&key.as_str()) {
warn!("Unknown config key '{}.{}' will be ignored.", path, key);
}
}
}
impl Default for MapEvalConfig {
fn default() -> Self {
Self {
enabled: true,
reuse_slab: true,
compile_expressions: true,
namespace_scope: MapNamespaceScope::FullMap,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum MapNamespaceScope {
#[default]
#[serde(rename = "full-map")]
FullMap,
#[serde(rename = "exports-only")]
ExportsOnly,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub enum PlotlyOutputType {
#[default]
#[cfg(feature = "plotly-html-handler")]
#[serde(rename = "plotly-html")]
PlotlyHtml,
#[cfg(feature = "plotly-svg-handler")]
#[serde(rename = "plotly-svg")]
PlotlySvg,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub enum PlotlyInputType {
#[default]
#[serde(rename = "json-input")]
JSONInput,
#[serde(rename = "toml-input")]
TOMLInput,
}