tomplate_build/types.rs
1//! Type definitions for the Tomplate build system.
2//!
3//! This module contains the core types used throughout the build system,
4//! including template definitions, error handling, and engine specifications.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::fmt;
9use std::path::PathBuf;
10use std::str::FromStr;
11
12/// A template definition from a `.tomplate.toml` file.
13///
14/// Templates are defined in TOML format with a required `template` field
15/// and optional `engine` and metadata fields.
16///
17/// # Examples
18///
19/// A typical template definition in TOML:
20///
21/// ```toml
22/// [my_template]
23/// template = "Hello {name}, welcome to {place}!"
24/// engine = "simple" # Optional
25///
26/// [complex_template]
27/// template = """
28/// {{#if logged_in}}
29/// Welcome back, {{username}}!
30/// {{else}}
31/// Please log in.
32/// {{/if}}
33/// """
34/// engine = "handlebars"
35/// ```
36#[derive(Debug, Clone, Deserialize, Serialize)]
37pub struct Template {
38 /// The template string containing the template pattern.
39 ///
40 /// This can use various syntaxes depending on the engine:
41 /// - Simple: `{variable}` placeholders
42 /// - Handlebars: `{{variable}}` with full Handlebars features
43 /// - Tera: `{{ variable }}` with Tera/Jinja2 syntax
44 /// - MiniJinja: Similar to Tera with Jinja2 syntax
45 pub template: String,
46
47 /// Optional template engine to use.
48 ///
49 /// If not specified, defaults to "simple" or the builder's default engine.
50 /// Valid values: "simple", "handlebars", "tera", "minijinja"
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub engine: Option<String>,
53
54 /// Additional metadata for the template.
55 ///
56 /// This can include custom fields for documentation, validation schemas,
57 /// or any other template-specific information. These fields are preserved
58 /// but not used by the core template system.
59 #[serde(flatten)]
60 pub metadata: HashMap<String, toml::Value>,
61}
62
63/// Error types for Tomplate build operations.
64///
65/// This enum represents all possible errors that can occur during
66/// template discovery, parsing, and amalgamation.
67#[derive(Debug, thiserror::Error)]
68pub enum Error {
69 /// I/O error occurred during file operations.
70 #[error("IO error: {0}")]
71 Io(#[from] std::io::Error),
72
73 /// Failed to parse TOML template file.
74 #[error("TOML parsing error: {0}")]
75 TomlParse(#[from] toml::de::Error),
76
77 /// Failed to serialize templates to TOML.
78 #[error("TOML serialization error: {0}")]
79 TomlSerialize(#[from] toml::ser::Error),
80
81 /// Invalid glob pattern provided.
82 #[error("Glob pattern error: {0}")]
83 Glob(#[from] glob::PatternError),
84
85 /// Referenced template file does not exist.
86 #[error("Template file not found: {0}")]
87 FileNotFound(PathBuf),
88
89 /// Found duplicate template names across files.
90 ///
91 /// Each template name must be unique across all discovered files.
92 #[error("Duplicate template name: {0}")]
93 DuplicateTemplate(String),
94
95 /// Template definition is invalid or malformed.
96 #[error("Invalid template definition: {0}")]
97 InvalidTemplate(String),
98
99 /// Referenced template not found in registry.
100 #[error("Template not found: {0}")]
101 TemplateNotFound(String),
102
103 /// Template engine error during processing.
104 #[error("Template engine error: {0}")]
105 EngineError(String),
106
107 /// Invalid parameter provided to template.
108 #[error("Invalid parameter: {0}")]
109 InvalidParameter(String),
110}
111
112/// Result type alias for Tomplate build operations.
113///
114/// A convenience type alias for `Result<T, tomplate_build::Error>`.
115pub type Result<T> = std::result::Result<T, Error>;
116
117/// Supported template engines for processing templates.
118///
119/// Each engine provides different features and syntax:
120///
121/// - **Simple**: Basic `{variable}` substitution
122/// - **Handlebars**: Full Handlebars templating with helpers and conditionals
123/// - **Tera**: Jinja2-like templating with filters and inheritance
124/// - **MiniJinja**: Lightweight Jinja2 implementation
125///
126/// # Examples
127///
128/// ```rust,ignore
129/// use tomplate_build::Engine;
130///
131/// // Use in Builder
132/// Builder::new()
133/// .default_engine(Engine::Handlebars)
134/// .build()?;
135/// ```
136///
137/// # Feature Flags
138///
139/// Some engines require feature flags to be enabled:
140/// - `handlebars`: Enables the Handlebars engine
141/// - `tera`: Enables the Tera engine
142/// - `minijinja`: Enables the MiniJinja engine
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
144#[serde(rename_all = "lowercase")]
145pub enum Engine {
146 /// Simple variable substitution engine.
147 ///
148 /// Uses `{variable}` syntax for placeholders.
149 /// This is the default engine and requires no additional features.
150 ///
151 /// # Example Template
152 /// ```text
153 /// Hello {name}, you have {count} messages.
154 /// ```
155 Simple,
156
157 /// Handlebars template engine.
158 ///
159 /// Provides full Handlebars functionality including:
160 /// - Conditionals: `{{#if}}`, `{{#unless}}`
161 /// - Loops: `{{#each}}`
162 /// - Helpers and partials
163 /// - HTML escaping (disabled by default in Tomplate)
164 ///
165 /// Requires the `handlebars` feature.
166 ///
167 /// # Example Template
168 /// ```text
169 /// {{#if logged_in}}
170 /// Welcome {{username}}!
171 /// {{else}}
172 /// Please log in.
173 /// {{/if}}
174 /// ```
175 #[cfg(feature = "handlebars")]
176 #[cfg_attr(docsrs, doc(cfg(feature = "handlebars")))]
177 Handlebars,
178
179 /// Tera template engine.
180 ///
181 /// Provides Jinja2-like syntax with:
182 /// - Variables: `{{ variable }}`
183 /// - Filters: `{{ value | upper }}`
184 /// - Control structures: `{% if %}`, `{% for %}`
185 /// - Template inheritance
186 ///
187 /// Requires the `tera` feature.
188 ///
189 /// # Example Template
190 /// ```text
191 /// {% for user in users %}
192 /// {{ user.name | upper }}
193 /// {% endfor %}
194 /// ```
195 #[cfg(feature = "tera")]
196 #[cfg_attr(docsrs, doc(cfg(feature = "tera")))]
197 Tera,
198
199 /// MiniJinja template engine.
200 ///
201 /// A lightweight Jinja2 implementation with:
202 /// - Similar syntax to Tera
203 /// - Good performance
204 /// - Smaller dependency footprint
205 ///
206 /// Requires the `minijinja` feature.
207 ///
208 /// # Example Template
209 /// ```text
210 /// {% if items %}
211 /// {% for item in items %}
212 /// - {{ item }}
213 /// {% endfor %}
214 /// {% endif %}
215 /// ```
216 #[cfg(feature = "minijinja")]
217 #[cfg_attr(docsrs, doc(cfg(feature = "minijinja")))]
218 MiniJinja,
219}
220
221impl Engine {
222 /// Returns the string representation of the engine.
223 ///
224 /// This is the value used in TOML files for the `engine` field.
225 ///
226 /// # Examples
227 ///
228 /// ```rust,ignore
229 /// assert_eq!(Engine::Simple.as_str(), "simple");
230 /// assert_eq!(Engine::Handlebars.as_str(), "handlebars");
231 /// ```
232 pub fn as_str(&self) -> &'static str {
233 match self {
234 Engine::Simple => "simple",
235 #[cfg(feature = "handlebars")]
236 Engine::Handlebars => "handlebars",
237 #[cfg(feature = "tera")]
238 Engine::Tera => "tera",
239 #[cfg(feature = "minijinja")]
240 Engine::MiniJinja => "minijinja",
241 }
242 }
243}
244
245impl Default for Engine {
246 /// Returns the default engine (Simple).
247 fn default() -> Self {
248 Engine::Simple
249 }
250}
251
252impl fmt::Display for Engine {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 f.write_str(self.as_str())
255 }
256}
257
258impl FromStr for Engine {
259 type Err = Error;
260
261 /// Parses an engine name from a string.
262 ///
263 /// # Examples
264 ///
265 /// ```rust,ignore
266 /// use std::str::FromStr;
267 /// use tomplate_build::Engine;
268 ///
269 /// let engine = Engine::from_str("handlebars")?;
270 /// assert_eq!(engine, Engine::Handlebars);
271 /// ```
272 ///
273 /// # Errors
274 ///
275 /// Returns an error if the engine name is unknown or the required
276 /// feature is not enabled.
277 fn from_str(s: &str) -> Result<Self> {
278 match s {
279 "simple" | "" => Ok(Engine::Simple),
280 #[cfg(feature = "handlebars")]
281 "handlebars" => Ok(Engine::Handlebars),
282 #[cfg(feature = "tera")]
283 "tera" => Ok(Engine::Tera),
284 #[cfg(feature = "minijinja")]
285 "minijinja" => Ok(Engine::MiniJinja),
286 _ => Err(Error::EngineError(format!("Unknown or disabled template engine: {}", s))),
287 }
288 }
289}