use std::collections::BTreeMap;
use serde_json::Value;
use textlane_core::Error;
use crate::helpers::registry::load_registry;
use crate::helpers::theme_table::THEMES;
use crate::models::highlight::{Theme, Themes};
use crate::models::options::{ColorReplacement, Highlight};
const DEFAULT_MODE: &str = "light";
pub(crate) fn resolve_themes(options: &Highlight) -> Result<Themes, Error> {
let Some(themes) = &options.themes else {
let Some(name) = &options.theme else {
return Err(invalid("either `theme` or `themes` must be provided"));
};
return Ok(Themes {
list: vec![find_theme(DEFAULT_MODE, name, options)?],
single: true,
});
};
let mut list = vec![];
for (mode, name) in themes {
match name {
Value::String(name) if !name.is_empty() => {
list.push(find_theme(mode, name, options)?);
}
Value::Null | Value::Bool(false) | Value::String(_) => {}
_ => return Err(invalid("a theme must be the name of one Shiki bundles")),
}
}
if list.is_empty() {
return Err(invalid("`themes` must not be empty"));
}
if !list.iter().any(|theme| theme.mode == DEFAULT_MODE) {
return Err(invalid("`themes` must have a `light` theme"));
}
list.sort_by_key(|theme| theme.mode != DEFAULT_MODE);
Ok(Themes {
list,
single: false,
})
}
fn find_theme(mode: &str, name: &str, options: &Highlight) -> Result<Theme, Error> {
let colors = THEMES.iter().find(|theme| theme.name == name);
let registry = load_registry().ok_or_else(|| {
Error::new("highlight-unavailable", "giallo's registry failed to load")
})?;
match colors {
Some(colors) if registry.contains_theme(name) => Ok(Theme {
mode: mode.to_owned(),
colors,
replacements: resolve_replacements(name, options),
}),
_ => Err(invalid(&format!("theme `{name}` is not one Shiki bundles"))),
}
}
fn resolve_replacements(name: &str, options: &Highlight) -> BTreeMap<String, String> {
let mut replacements = BTreeMap::new();
for (key, value) in &options.color_replacements {
match value {
ColorReplacement::Color(color) => {
replacements.insert(key.clone(), color.clone());
}
ColorReplacement::Theme(map) if key == name => {
replacements.extend(map.iter().map(|(a, b)| (a.clone(), b.clone())));
}
ColorReplacement::Theme(_) => {}
}
}
replacements
}
fn invalid(message: &str) -> Error {
Error::new("invalid-options", format!("`html.highlight`: {message}"))
}