rusty_handlebars/
lib.rs

1//! Rusty Handlebars - A type-safe Handlebars templating engine for Rust
2//!
3//! This crate provides a type-safe implementation of the Handlebars templating engine
4//! with a focus on compile-time template processing and HTML safety.
5//!
6//! # Features
7//!
8//! - Type-safe templating with compile-time validation
9//! - HTML escaping for secure output
10//! - Optional HTML minification
11//! - Derive macro support for easy integration
12//! - Flexible display traits for both regular and HTML-safe output
13//! - Optional parser functionality
14//!
15//! # Examples
16//!
17//! ```rust
18//! use rusty_handlebars::WithRustyHandlebars;
19//!
20//! #[derive(WithRustyHandlebars)]
21//! #[template(path = "templates/hello.hbs")]
22//! struct HelloTemplate {
23//!     name: String,
24//! }
25//! ```
26//!
27//! For HTML-safe output:
28//!
29//! ```rust
30//! use rusty_handlebars::AsDisplayHtml;
31//!
32//! let html = "<script>alert('xss')</script>";
33//! let safe_html = html.as_display_html().to_string();
34//! // Output: &lt;script&gt;alert(&#x27;xss&#x27;)&lt;/script&gt;
35//! ```
36
37use std::fmt::{Display, Write};
38
39pub mod as_bool;
40pub use as_bool::AsBool;
41
42/// Derive macro for implementing Handlebars templating on structs
43pub use rusty_handlebars_derive::WithRustyHandlebars;
44
45#[cfg(feature = "parser")]
46pub use rusty_handlebars_parser::{Compiler, Options};
47
48/// Trait that must be implemented for types that can be used with Handlebars templates
49pub trait WithRustyHandlebars : Display{}
50
51macro_rules! impl_as_display {
52    ($($t:ty),*) => {
53        $(
54            impl AsDisplay for $t{
55                fn as_display(&self) -> impl Display {
56                    self
57                }
58            }
59
60            impl AsDisplay for &$t{
61                fn as_display(&self) -> impl Display {
62                    self
63                }
64            }
65        )*
66    }
67}
68
69macro_rules! impl_as_display_html {
70    ($($t:ty),*) => {
71        $(
72            impl AsDisplayHtml for $t{
73                fn as_display_html(&self) -> impl Display {
74                    self
75                }
76            }
77
78            impl AsDisplayHtml for &$t{
79                fn as_display_html(&self) -> impl Display {
80                    self
81                }
82            }
83        )*
84    }
85}
86
87/// Trait for converting values to a Display implementation
88pub trait AsDisplay{
89    /// Returns a type that implements Display
90    fn as_display(&self) -> impl Display;
91}
92
93/// Trait for converting values to an HTML-safe Display implementation
94pub trait AsDisplayHtml{
95    /// Returns a type that implements Display with HTML escaping
96    fn as_display_html(&self) -> impl Display;
97}
98
99impl_as_display!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, String, &str, bool);
100
101impl_as_display_html!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool);
102
103impl<T: AsDisplay> AsDisplay for Option<T> {
104    fn as_display(&self) -> impl Display {
105        match self{
106            Some(t) => t.as_display().to_string(),
107            None => "".as_display().to_string()
108        }
109    }
110}
111
112impl<T: AsDisplay> AsDisplay for &Option<T>{
113    fn as_display(&self) -> impl Display {
114        match self{
115            Some(t) => t.as_display().to_string(),
116            None => "".as_display().to_string()
117        }
118    }
119}
120
121impl<T: AsDisplay> AsDisplay for Box<T>{
122    fn as_display(&self) -> impl Display {
123        self.as_ref().as_display()
124    }
125}
126
127impl<T: AsDisplay> AsDisplay for &Box<T>{
128    fn as_display(&self) -> impl Display {
129        self.as_ref().as_display()
130    }
131}
132
133struct DisplayHtml<'a>{
134    string: &'a str
135}
136
137impl<'a> Display for DisplayHtml<'a>{
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        for c in self.string.chars(){
140            match c{
141                '&' => f.write_str("&amp;")?,
142                '<' => f.write_str("&lt;")?,
143                '>' => f.write_str("&gt;")?,
144                '"' => f.write_str("&quot;")?,
145                c => f.write_char(c)?
146            }
147        }
148        Ok(())
149    }
150}
151
152impl AsDisplayHtml for &str{
153    fn as_display_html(&self) -> impl Display {
154        DisplayHtml{string: self}
155    }
156}
157
158impl AsDisplayHtml for &&str{
159    fn as_display_html(&self) -> impl Display {
160        DisplayHtml{string: *self}
161    }
162}
163
164impl AsDisplayHtml for String{
165    fn as_display_html(&self) -> impl Display {
166        DisplayHtml{string: self.as_str()}
167    }
168}
169
170impl<T: AsDisplayHtml> AsDisplayHtml for Option<T>{
171    fn as_display_html(&self) -> impl Display {
172        match self{
173            Some(t) => t.as_display_html().to_string(),
174            None => "".to_string()
175        }
176    }
177}
178
179impl<T: AsDisplayHtml> AsDisplayHtml for &Option<T>{
180    fn as_display_html(&self) -> impl Display {
181        match self{
182            Some(t) => t.as_display_html().to_string(),
183            None => "".to_string()
184        }
185    }
186}
187
188impl<T: AsDisplayHtml> AsDisplayHtml for Box<T>{
189    fn as_display_html(&self) -> impl Display {
190        self.as_ref().as_display_html()
191    }
192}
193
194impl<T: AsDisplayHtml> AsDisplayHtml for &Box<T>{
195    fn as_display_html(&self) -> impl Display {
196        self.as_ref().as_display_html()
197    }
198}