use std::{fs, path::Path};
use toml::Value;
#[derive(Debug, Clone)]
pub struct TemplateConfig {
pub name: String,
pub colors: TemplateColors,
pub fonts: TemplateFonts,
pub features: TemplateFeatures,
pub i18n: TemplateI18n,
}
#[derive(Debug, Clone)]
pub struct TemplateColors {
pub primary_color: String,
pub secondary_color: String,
pub background_color: String,
pub text_color: String,
pub code_background: String,
pub border_color: String,
pub primary_color_dark: String,
pub secondary_color_dark: String,
pub background_color_dark: String,
pub text_color_dark: String,
pub code_background_dark: String,
pub border_color_dark: String,
}
#[derive(Debug, Clone)]
pub struct TemplateFonts {
pub font_family: String,
pub code_font_family: String,
}
#[derive(Debug, Clone)]
pub struct TemplateFeatures {
pub enable_mermaid: bool,
pub enable_highlight: bool,
pub highlight_theme: String,
}
#[derive(Debug, Clone)]
pub struct TemplateI18n {
pub default_lang: String,
}
#[derive(Debug, Clone)]
pub struct Template {
pub name: String,
pub config: TemplateConfig,
pub content: String,
}
impl Template {
pub fn load_from_dir(template_dir: &str) -> Result<Self, Box<dyn std::error::Error>> {
let template_path = Path::new(template_dir).join("template.html");
let content = fs::read_to_string(template_path)?;
let config_path = Path::new(template_dir).join("config.toml");
let config_content = fs::read_to_string(config_path)?;
let config_value: Value = toml::from_str(&config_content)?;
let config = Self::parse_config(&config_value)?;
Ok(Self { name: config.name.clone(), config, content })
}
fn parse_config(config_value: &Value) -> Result<TemplateConfig, Box<dyn std::error::Error>> {
let theme = config_value.get("theme").ok_or("Missing theme config")?;
let name = theme.get("name").and_then(|v| v.as_str()).unwrap_or("default").to_string();
let colors = theme.get("colors").ok_or("Missing colors config")?;
let colors = TemplateColors {
primary_color: colors.get("primary_color").and_then(|v| v.as_str()).unwrap_or("#3498db").to_string(),
secondary_color: colors.get("secondary_color").and_then(|v| v.as_str()).unwrap_or("#2c3e50").to_string(),
background_color: colors.get("background_color").and_then(|v| v.as_str()).unwrap_or("#ffffff").to_string(),
text_color: colors.get("text_color").and_then(|v| v.as_str()).unwrap_or("#333333").to_string(),
code_background: colors.get("code_background").and_then(|v| v.as_str()).unwrap_or("#f4f4f4").to_string(),
border_color: colors.get("border_color").and_then(|v| v.as_str()).unwrap_or("#e0e0e0").to_string(),
primary_color_dark: colors.get("primary_color_dark").and_then(|v| v.as_str()).unwrap_or("#3498db").to_string(),
secondary_color_dark: colors.get("secondary_color_dark").and_then(|v| v.as_str()).unwrap_or("#ecf0f1").to_string(),
background_color_dark: colors.get("background_color_dark").and_then(|v| v.as_str()).unwrap_or("#1a1a1a").to_string(),
text_color_dark: colors.get("text_color_dark").and_then(|v| v.as_str()).unwrap_or("#e0e0e0").to_string(),
code_background_dark: colors.get("code_background_dark").and_then(|v| v.as_str()).unwrap_or("#2d2d2d").to_string(),
border_color_dark: colors.get("border_color_dark").and_then(|v| v.as_str()).unwrap_or("#3d3d3d").to_string(),
};
let fonts = theme.get("fonts").ok_or("Missing fonts config")?;
let fonts = TemplateFonts { font_family: fonts.get("font_family").and_then(|v| v.as_str()).unwrap_or("-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif").to_string(), code_font_family: fonts.get("code_font_family").and_then(|v| v.as_str()).unwrap_or("'Courier New', Courier, monospace").to_string() };
let features = theme.get("features").ok_or("Missing features config")?;
let features = TemplateFeatures { enable_mermaid: features.get("enable_mermaid").and_then(|v| v.as_bool()).unwrap_or(true), enable_highlight: features.get("enable_highlight").and_then(|v| v.as_bool()).unwrap_or(true), highlight_theme: features.get("highlight_theme").and_then(|v| v.as_str()).unwrap_or("github").to_string() };
let i18n = theme.get("i18n").ok_or("Missing i18n config")?;
let i18n = TemplateI18n { default_lang: i18n.get("default_lang").and_then(|v| v.as_str()).unwrap_or("zh-CN").to_string() };
Ok(TemplateConfig { name, colors, fonts, features, i18n })
}
pub fn render(&self, title: &str, content: &str, sidebar: &str) -> String {
let mut rendered = self.content.clone();
rendered = rendered.replace("{{ title }}", title);
rendered = rendered.replace("{{ content }}", content);
rendered = rendered.replace("{{ sidebar }}", sidebar);
rendered = rendered.replace("{{ lang }}", &self.config.i18n.default_lang);
rendered = rendered.replace("{{ primary_color }}", &self.config.colors.primary_color);
rendered = rendered.replace("{{ secondary_color }}", &self.config.colors.secondary_color);
rendered = rendered.replace("{{ background_color }}", &self.config.colors.background_color);
rendered = rendered.replace("{{ text_color }}", &self.config.colors.text_color);
rendered = rendered.replace("{{ code_background }}", &self.config.colors.code_background);
rendered = rendered.replace("{{ border_color }}", &self.config.colors.border_color);
rendered = rendered.replace("{{ primary_color_dark }}", &self.config.colors.primary_color_dark);
rendered = rendered.replace("{{ secondary_color_dark }}", &self.config.colors.secondary_color_dark);
rendered = rendered.replace("{{ background_color_dark }}", &self.config.colors.background_color_dark);
rendered = rendered.replace("{{ text_color_dark }}", &self.config.colors.text_color_dark);
rendered = rendered.replace("{{ code_background_dark }}", &self.config.colors.code_background_dark);
rendered = rendered.replace("{{ border_color_dark }}", &self.config.colors.border_color_dark);
rendered = rendered.replace("{{ font_family }}", &self.config.fonts.font_family);
rendered = rendered.replace("{{ code_font_family }}", &self.config.fonts.code_font_family);
rendered = rendered.replace("{{ enable_mermaid }}", &self.config.features.enable_mermaid.to_string());
rendered = rendered.replace("{{ enable_highlight }}", &self.config.features.enable_highlight.to_string());
rendered = rendered.replace("{{ highlight_theme }}", &self.config.features.highlight_theme);
if !self.config.features.enable_mermaid {
rendered = rendered.replace("{% if enable_mermaid %}", "");
rendered = rendered.replace("{% endif %}", "");
}
else {
rendered = rendered.replace("{% if enable_mermaid %}", "");
rendered = rendered.replace("{% endif %}", "");
}
if !self.config.features.enable_highlight {
rendered = rendered.replace("{% if enable_highlight %}", "");
rendered = rendered.replace("{% endif %}", "");
}
else {
rendered = rendered.replace("{% if enable_highlight %}", "");
rendered = rendered.replace("{% endif %}", "");
}
rendered
}
}