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}