origami_engine/
lib.rs

1//! # Origami Engine
2//! A Rust templating engine that allows for rendering HTML elements and building reusable components.
3//!
4//! ## Props Passing
5//!
6//! You can pass props to components to customize their behavior and appearance. Below is an example of a homepage and an about page, both utilizing a button component with various attributes.
7//! ```rust
8//! use origami_engine::comp;
9//!
10//! // Define a button component that takes props
11//! comp! {
12//!     button_component(attr, label) =>
13//!     button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" @attr; {
14//!         @label;
15//!     }
16//! }
17//!
18//! // Define the homepage component
19//! comp! {
20//!     home =>
21//!     div {
22//!         h1 { "Welcome to the Homepage!" }
23//!         // Use the button_component with props
24//!         call button_component { attr { onclick="alert('clicked')" }, label { "Click Me" } }
25//!     }
26//! }
27//!
28//! let html = home!();
29//!
30//! assert_eq!(
31//!     html.0,
32//!     r#"<div><h1>Welcome to the Homepage!</h1><button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" onclick="alert('clicked')">Click Me</button></div>"#
33//! );
34//!
35//! // Define the about page component
36//! comp! {
37//!     about =>
38//!     div {
39//!         h1 { "About Us" }
40//!         p { "We are committed to delivering quality service." }
41//!         // Use the button_component with props
42//!         call button_component { attr { onclick="alert('clicked learn more')" }, label { "Learn More" } }
43//!     }
44//! }
45//!
46//! let html = about!();
47//! assert_eq!(
48//!     html.0,
49//!     r#"<div><h1>About Us</h1><p>We are committed to delivering quality service.</p><button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" onclick="alert('clicked learn more')">Learn More</button></div>"#
50//! );
51//! ```
52//!
53//! ## Layout
54//!
55//! You can create a layout structure that includes a navigation bar, a body for dynamic content, and a footer. Below is an example demonstrating this layout.
56//!
57//! ```rust
58//! use origami_engine::comp;
59//!
60//! // Define a layout component with a navigation bar, body, and footer
61//! comp! {
62//!     layout_component(content) =>
63//!     // Navigation bar
64//!     nav {
65//!         ul {
66//!             li { a { "Home" } }
67//!             li { a { "About" } }
68//!             li { a { "Contact" } }
69//!         }
70//!     }
71//!     // Body placeholder for dynamic content
72//!     main {
73//!         @content;
74//!     }
75//!     // Footer
76//!     footer {
77//!         p { "© 2024 Your Company" }
78//!     }
79//! }
80//!
81//! // Define the homepage component using the layout
82//! comp! {
83//!     home =>
84//!     call layout_component {
85//!         content {
86//!             h1 { "Welcome to the Homepage!" }
87//!             p { "This is the main content of the homepage." }
88//!         }
89//!     }
90//! }
91//!
92//! let html = home!(cap => 250); // It is recommended to provide `cap`, i.e., the maximum length of html
93//!                              // to avoid unnecessary reallocations of strings
94//! assert_eq!(
95//!     html.0,
96//!     r#"<nav><ul><li><a>Home</a></li><li><a>About</a></li><li><a>Contact</a></li></ul></nav><main><h1>Welcome to the Homepage!</h1><p>This is the main content of the homepage.</p></main><footer><p>© 2024 Your Company</p></footer>"#
97//! );
98//!
99//! // Define the about page component using the layout
100//! comp! {
101//!     about =>
102//!     call layout_component {
103//!         content {
104//!             h1 { "About Us" }
105//!             p { "We are committed to delivering quality service." }
106//!         }
107//!     }
108//! }
109//!
110//! let html = about!(cap => 250); // It is recommended to provide `cap`, i.e., the maximum length of html
111//!                              // to avoid unnecessary reallocations of strings
112//! assert_eq!(
113//!     html.0,
114//!     r#"<nav><ul><li><a>Home</a></li><li><a>About</a></li><li><a>Contact</a></li></ul></nav><main><h1>About Us</h1><p>We are committed to delivering quality service.</p></main><footer><p>© 2024 Your Company</p></footer>"#
115//! );
116//! ```
117//! ## Escape and Noescape
118//!
119//! You can use `escape` and `noescape` to control HTML escaping behavior in the template (`html_escape` is feature is required):
120//!
121//! ```rust
122//! #[cfg(feature = "html_escape")]
123//! {
124//!     use origami_engine::comp;
125//!
126//!     comp! {
127//!         foo =>
128//!         div noescape {
129//!             div { "<div>Unsafe HTML</div>" } // Inherited, this will not be escaped
130//!             div escape {
131//!                 "<div>Safe HTML</div>" // This will be escaped
132//!             }
133//!         }
134//!     }
135//!
136//!     let html = foo!();
137//!     assert_eq!(html.0, "<div><div><div>Unsafe HTML</div></div><div>&lt;div&gt;Safe HTML&lt;/div&gt;</div></div>");
138//! }
139//! ```
140//!
141//! You can also use `noescape` or `escape` with conditional rendering:
142//!
143//! ```rust
144//! #[cfg(feature = "html_escape")]
145//! {
146//!     use origami_engine::comp;
147//!
148//!     let text = "bar";
149//!
150//!     comp! {
151//!         foo =>
152//!         div noescape {
153//!             if text == "foo"; noescape {
154//!                 "<div>Unsafe HTML</div>"
155//!             } else if text == "bar"; escape {
156//!                 "<div>Safe HTML</div>"
157//!             } else noescape {
158//!                 "<div>Default HTML</div>"
159//!             }
160//!         }
161//!     }
162//!
163//!     let html = foo!();
164//!     assert_eq!(html.0, "<div>&lt;div&gt;Safe HTML&lt;/div&gt;</div>");
165//! }
166//! ```
167//!
168//! Or with expressions:
169//!
170//! ```rust
171//! #[cfg(feature = "html_escape")]
172//! {
173//!     use origami_engine::comp;
174//!
175//!     let text = "<div>foo</div>";
176//!     comp! {
177//!         foo =>
178//!         div { @text;! }
179//!     }
180//!
181//!     let html = foo!();
182//!     assert_eq!(html.0, "<div><div>foo</div></div>");
183//! }
184//! ```
185//!
186//! Or with literals:
187//!
188//! ```rust
189//! #[cfg(feature = "html_escape")]
190//! {
191//!     use origami_engine::comp;
192//!
193//!     comp! {
194//!         foo =>
195//!         div {
196//!             "<div>foo</div>"!
197//!         }
198//!     }
199//!
200//!     let html = foo!();
201//!     assert_eq!(html.0, "<div><div>foo</div></div>");
202//! }
203//! ```
204//!
205//! Or match expressions:
206//! ```rust
207//! #[cfg(feature = "html_escape")]
208//! {
209//!     use origami_engine::comp;
210//!
211//!     let text = "foo";
212//!     comp! {
213//!         foo =>
214//!         div {
215//!             match text; noescape {
216//!                 "foo" => {
217//!                     "<div>foo</div>" // Inherited, this will not be escaped
218//!                 },
219//!                 _ => escape {
220//!                     "<div>foo</div>" // This will be escaped
221//!                 }
222//!             }
223//!         }
224//!     }
225//!
226//!     let html = foo!();
227//!     assert_eq!(html.0, "<div><div>foo</div></div>");
228//! }
229//! ```
230
231pub use origami_macros::anon;
232pub use origami_macros::comp;
233
234#[derive(Debug, Clone)]
235pub struct Origami(pub String);
236
237#[cfg(feature = "html_escape")]
238#[doc(no_inline)]
239pub use html_escape::encode_text_to_string;
240
241#[cfg(feature = "minify_html")]
242#[doc(no_inline)]
243pub use minify_html::*;
244
245#[cfg(feature = "axum")]
246use ::axum::response::{Html, IntoResponse, Response};
247#[cfg(feature = "axum")]
248impl IntoResponse for Origami {
249    fn into_response(self) -> Response {
250        Html(self.0).into_response()
251    }
252}