Skip to main content

nargo_document/templates/
mod.rs

1//! 模板系统模块
2//! 提供模板管理和渲染功能
3//!
4//! 本模块兼容旧的 Template 系统,同时支持新的主题系统
5
6use std::{fs, path::Path};
7use toml::Value;
8
9/// 模板配置结构
10#[derive(Debug, Clone)]
11pub struct TemplateConfig {
12    /// 主题名称
13    pub name: String,
14    /// 主题颜色
15    pub colors: TemplateColors,
16    /// 字体配置
17    pub fonts: TemplateFonts,
18    /// 功能开关
19    pub features: TemplateFeatures,
20    /// 语言设置
21    pub i18n: TemplateI18n,
22}
23
24/// 模板颜色配置
25#[derive(Debug, Clone)]
26pub struct TemplateColors {
27    /// 亮色模式主色
28    pub primary_color: String,
29    /// 亮色模式次要色
30    pub secondary_color: String,
31    /// 亮色模式背景色
32    pub background_color: String,
33    /// 亮色模式文本色
34    pub text_color: String,
35    /// 亮色模式代码背景色
36    pub code_background: String,
37    /// 亮色模式边框色
38    pub border_color: String,
39    /// 暗色模式主色
40    pub primary_color_dark: String,
41    /// 暗色模式次要色
42    pub secondary_color_dark: String,
43    /// 暗色模式背景色
44    pub background_color_dark: String,
45    /// 暗色模式文本色
46    pub text_color_dark: String,
47    /// 暗色模式代码背景色
48    pub code_background_dark: String,
49    /// 暗色模式边框色
50    pub border_color_dark: String,
51}
52
53/// 模板字体配置
54#[derive(Debug, Clone)]
55pub struct TemplateFonts {
56    /// 字体系列
57    pub font_family: String,
58    /// 代码字体系列
59    pub code_font_family: String,
60}
61
62/// 模板功能开关
63#[derive(Debug, Clone)]
64pub struct TemplateFeatures {
65    /// 是否启用 Mermaid 图表
66    pub enable_mermaid: bool,
67    /// 是否启用语法高亮
68    pub enable_highlight: bool,
69    /// 语法高亮主题
70    pub highlight_theme: String,
71}
72
73/// 模板语言设置
74#[derive(Debug, Clone)]
75pub struct TemplateI18n {
76    /// 默认语言
77    pub default_lang: String,
78}
79
80/// 模板结构(旧版本兼容性)
81#[derive(Debug, Clone)]
82pub struct Template {
83    /// 模板名称
84    pub name: String,
85    /// 模板配置
86    pub config: TemplateConfig,
87    /// 模板内容
88    pub content: String,
89}
90
91impl Template {
92    /// 从目录加载模板
93    ///
94    /// # 参数
95    /// * `template_dir` - 模板目录路径
96    ///
97    /// # 返回值
98    /// 返回 Result,成功时为 Template,失败时为错误信息
99    pub fn load_from_dir(template_dir: &str) -> Result<Self, Box<dyn std::error::Error>> {
100        let template_path = Path::new(template_dir).join("template.html");
101        let content = fs::read_to_string(template_path)?;
102
103        let config_path = Path::new(template_dir).join("config.toml");
104        let config_content = fs::read_to_string(config_path)?;
105        let config_value: Value = toml::from_str(&config_content)?;
106
107        let config = Self::parse_config(&config_value)?;
108
109        Ok(Self { name: config.name.clone(), config, content })
110    }
111
112    /// 解析模板配置
113    ///
114    /// # 参数
115    /// * `config_value` - 配置值
116    ///
117    /// # 返回值
118    /// 返回 Result,成功时为 TemplateConfig,失败时为错误信息
119    fn parse_config(config_value: &Value) -> Result<TemplateConfig, Box<dyn std::error::Error>> {
120        let theme = config_value.get("theme").ok_or("Missing theme config")?;
121
122        let name = theme.get("name").and_then(|v| v.as_str()).unwrap_or("default").to_string();
123
124        let colors = theme.get("colors").ok_or("Missing colors config")?;
125        let colors = TemplateColors {
126            primary_color: colors.get("primary_color").and_then(|v| v.as_str()).unwrap_or("#3498db").to_string(),
127            secondary_color: colors.get("secondary_color").and_then(|v| v.as_str()).unwrap_or("#2c3e50").to_string(),
128            background_color: colors.get("background_color").and_then(|v| v.as_str()).unwrap_or("#ffffff").to_string(),
129            text_color: colors.get("text_color").and_then(|v| v.as_str()).unwrap_or("#333333").to_string(),
130            code_background: colors.get("code_background").and_then(|v| v.as_str()).unwrap_or("#f4f4f4").to_string(),
131            border_color: colors.get("border_color").and_then(|v| v.as_str()).unwrap_or("#e0e0e0").to_string(),
132            primary_color_dark: colors.get("primary_color_dark").and_then(|v| v.as_str()).unwrap_or("#3498db").to_string(),
133            secondary_color_dark: colors.get("secondary_color_dark").and_then(|v| v.as_str()).unwrap_or("#ecf0f1").to_string(),
134            background_color_dark: colors.get("background_color_dark").and_then(|v| v.as_str()).unwrap_or("#1a1a1a").to_string(),
135            text_color_dark: colors.get("text_color_dark").and_then(|v| v.as_str()).unwrap_or("#e0e0e0").to_string(),
136            code_background_dark: colors.get("code_background_dark").and_then(|v| v.as_str()).unwrap_or("#2d2d2d").to_string(),
137            border_color_dark: colors.get("border_color_dark").and_then(|v| v.as_str()).unwrap_or("#3d3d3d").to_string(),
138        };
139
140        let fonts = theme.get("fonts").ok_or("Missing fonts config")?;
141        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() };
142
143        let features = theme.get("features").ok_or("Missing features config")?;
144        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() };
145
146        let i18n = theme.get("i18n").ok_or("Missing i18n config")?;
147        let i18n = TemplateI18n { default_lang: i18n.get("default_lang").and_then(|v| v.as_str()).unwrap_or("zh-CN").to_string() };
148
149        Ok(TemplateConfig { name, colors, fonts, features, i18n })
150    }
151
152    /// 渲染模板
153    ///
154    /// # 参数
155    /// * `title` - 页面标题
156    /// * `content` - 页面内容
157    /// * `sidebar` - 侧边栏内容
158    ///
159    /// # 返回值
160    /// 返回渲染后的 HTML 字符串
161    pub fn render(&self, title: &str, content: &str, sidebar: &str) -> String {
162        let mut rendered = self.content.clone();
163
164        rendered = rendered.replace("{{ title }}", title);
165        rendered = rendered.replace("{{ content }}", content);
166        rendered = rendered.replace("{{ sidebar }}", sidebar);
167        rendered = rendered.replace("{{ lang }}", &self.config.i18n.default_lang);
168
169        rendered = rendered.replace("{{ primary_color }}", &self.config.colors.primary_color);
170        rendered = rendered.replace("{{ secondary_color }}", &self.config.colors.secondary_color);
171        rendered = rendered.replace("{{ background_color }}", &self.config.colors.background_color);
172        rendered = rendered.replace("{{ text_color }}", &self.config.colors.text_color);
173        rendered = rendered.replace("{{ code_background }}", &self.config.colors.code_background);
174        rendered = rendered.replace("{{ border_color }}", &self.config.colors.border_color);
175        rendered = rendered.replace("{{ primary_color_dark }}", &self.config.colors.primary_color_dark);
176        rendered = rendered.replace("{{ secondary_color_dark }}", &self.config.colors.secondary_color_dark);
177        rendered = rendered.replace("{{ background_color_dark }}", &self.config.colors.background_color_dark);
178        rendered = rendered.replace("{{ text_color_dark }}", &self.config.colors.text_color_dark);
179        rendered = rendered.replace("{{ code_background_dark }}", &self.config.colors.code_background_dark);
180        rendered = rendered.replace("{{ border_color_dark }}", &self.config.colors.border_color_dark);
181
182        rendered = rendered.replace("{{ font_family }}", &self.config.fonts.font_family);
183        rendered = rendered.replace("{{ code_font_family }}", &self.config.fonts.code_font_family);
184
185        rendered = rendered.replace("{{ enable_mermaid }}", &self.config.features.enable_mermaid.to_string());
186        rendered = rendered.replace("{{ enable_highlight }}", &self.config.features.enable_highlight.to_string());
187        rendered = rendered.replace("{{ highlight_theme }}", &self.config.features.highlight_theme);
188
189        if !self.config.features.enable_mermaid {
190            rendered = rendered.replace("{% if enable_mermaid %}", "");
191            rendered = rendered.replace("{% endif %}", "");
192        }
193        else {
194            rendered = rendered.replace("{% if enable_mermaid %}", "");
195            rendered = rendered.replace("{% endif %}", "");
196        }
197
198        if !self.config.features.enable_highlight {
199            rendered = rendered.replace("{% if enable_highlight %}", "");
200            rendered = rendered.replace("{% endif %}", "");
201        }
202        else {
203            rendered = rendered.replace("{% if enable_highlight %}", "");
204            rendered = rendered.replace("{% endif %}", "");
205        }
206
207        rendered
208    }
209}