### Render Mustache Templates with Rust Data Structures Source: https://github.com/maciejhirsz/ramhorns/blob/master/README.md Demonstrates how to define Rust structs with the `Content` derive macro and use them to render Mustache templates. This example shows basic variable substitution, iterating over sections, and handling inverse sections. ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Post<'a> { title: &'a str, teaser: &'a str, } #[derive(Content)] struct Blog<'a> { title: String, // Strings are cool posts: Vec>, // &'a [Post<'a>] would work too } // Standard Mustache action here let source = "

{{title}}

\ {{#posts}}

{{title}}

{{teaser}}

{{/posts}}\ {{^posts}}

No posts yet :(

{{/posts}}"; let tpl = Template::new(source).unwrap(); let rendered = tpl.render(&Blog { title: "My Awesome Blog!".to_string(), posts: vec![ Post { title: "How I tried Ramhorns and found love 💖", teaser: "This can happen to you too", }, Post { title: "Rust is kinda awesome", teaser: "Yes, even the borrow checker! 🦀", }, ] }); assert_eq!(rendered, "

My Awesome Blog!

\
\

How I tried Ramhorns and found love 💖

\

This can happen to you too

\
\
\

Rust is kinda awesome

\

Yes, even the borrow checker! 🦀

\
"); ``` -------------------------------- ### Load Templates from Folder with Partials Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Loads all .html templates from a specified folder. Supports partial template inclusion using `{{>partial.html}}` syntax. Ensure the template files exist in the specified directory. ```rust use ramhorns::{Ramhorns, Content}; #[derive(Content)] struct Page<'a> { title: &'a str, content: &'a str, } fn main() -> Result<(), ramhorns::Error> { // Load all .html templates from a folder let tpls: Ramhorns = Ramhorns::from_folder("./templates")?; // Get a specific template if let Some(tpl) = tpls.get("page.html") { let page = Page { title: "Welcome", content: "Hello World!", }; println!("{}", tpl.render(&page)); } Ok(()) } // Example: templates/page.html // {{>header.html}} //
//

{{title}}

//
{{content}}
//
// {{>footer.html}} // Example: templates/header.html //
// Example: templates/footer.html // ``` -------------------------------- ### Template::render Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Renders a template to a String using provided content. ```APIDOC ## Template::render ### Description Renders a template with the provided content implementing the Content trait. Returns a String with the rendered output, automatically escaping HTML special characters in variables. ### Parameters #### Request Body - **content** (Content trait) - Required - The data structure implementing the Content trait to be rendered. ``` -------------------------------- ### Template::new Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Creates a new Template instance from a Mustache source string. ```APIDOC ## Template::new ### Description Creates a new Template from a source string. The template parses Mustache syntax including variables, sections, and inverse sections. ### Parameters #### Request Body - **source** (String) - Required - The Mustache template source string. ``` -------------------------------- ### Template::render_to_file Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Renders the template output directly to a file path. ```APIDOC ## Template::render_to_file ### Description Renders the template output directly to a file path, using buffered IO for efficient writing. ### Parameters #### Request Body - **path** (String/Path) - Required - The file path to write to. - **content** (Content trait) - Required - The data structure implementing the Content trait. ``` -------------------------------- ### Custom File Extensions for Templates Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Loads templates from a folder using a custom file extension instead of the default `.html`. This allows for flexibility in organizing template files. ```rust use ramhorns::{Ramhorns, Content}; #[derive(Content)] struct Email { recipient: String, subject: String, body: String, } fn main() -> Result<(), ramhorns::Error> { // Load all .mustache files from templates folder let tpls: Ramhorns = Ramhorns::from_folder_with_extension("./templates", "mustache")?; let email = Email { recipient: "user@example.com".to_string(), subject: "Welcome!".to_string(), body: "Thank you for signing up.".to_string(), }; if let Some(tpl) = tpls.get("welcome.mustache") { println!("{}", tpl.render(&email)); } Ok(()) } ``` -------------------------------- ### Create a Template from String Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Initializes a new Template instance from a Mustache-formatted string. The data structure must implement the Content trait via the derive macro. ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Post { title: String, body: String, } fn main() -> Result<(), ramhorns::Error> { // Create a template from a string let source = "

{{title}}

{{body}}
"; let tpl = Template::new(source)?; let post = Post { title: "Hello World".to_string(), body: "This is my first post!".to_string(), }; let rendered = tpl.render(&post); // Output: "

Hello World

This is my first post!
" println!("{}", rendered); Ok(()) } ``` -------------------------------- ### Render Template Directly to File Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Writes the rendered template output directly to a specified file path using buffered IO. ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Report { title: String, sections: Vec
, } #[derive(Content)] struct Section { heading: String, body: String, } fn main() -> Result<(), Box> { let source = "# {{title}}\n\n{{#sections}}## {{heading}}\n{{body}}\n\n{{/sections}}"; let tpl = Template::new(source)?; let report = Report { title: "Annual Report".to_string(), sections: vec![ Section { heading: "Introduction".to_string(), body: "This year was productive.".to_string(), }, Section { heading: "Conclusion".to_string(), body: "Looking forward to next year.".to_string(), }, ], }; // Write rendered output directly to file tpl.render_to_file("report.md", &report)?; Ok(()) } ``` -------------------------------- ### Render Template with HashMap Content Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Renders a template using a `HashMap` as the content source. Keys in the `HashMap` correspond to variable names in the template. ```rust use ramhorns::Template; use std::collections::HashMap; fn main() -> Result<(), ramhorns::Error> { let source = "

{{title}}

{{description}}

"; let tpl = Template::new(source)?; // Using HashMap let mut map = HashMap::new(); map.insert("title", "Dynamic Title"); map.insert("description", "This content comes from a HashMap"); println!("{}", tpl.render(&map)); Ok(()) } ``` -------------------------------- ### Render Template with BTreeMap Content Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Renders a template using a `BTreeMap` as the content source. `BTreeMap` provides ordered keys, which can be relevant for certain template rendering scenarios. ```rust use ramhorns::Template; use std::collections::BTreeMap; fn main() -> Result<(), ramhorns::Error> { let source = "

{{title}}

{{description}}

"; let tpl = Template::new(source)?; // Using BTreeMap (ordered keys) let mut btree = BTreeMap::new(); btree.insert("title", "Ordered Title"); btree.insert("description", "This content comes from a BTreeMap"); println!("{}", tpl.render(&btree)); Ok(()) } ``` -------------------------------- ### Template::render_to_writer Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Renders the template directly to an IO writer. ```APIDOC ## Template::render_to_writer ### Description Renders the template directly to any type implementing std::io::Write, useful for streaming output or writing to files without intermediate string allocation. ### Parameters #### Request Body - **writer** (std::io::Write) - Required - The destination writer. - **content** (Content trait) - Required - The data structure implementing the Content trait. ``` -------------------------------- ### Render Template to IO Writer Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Streams rendered output directly to any type implementing std::io::Write, avoiding intermediate string allocations. ```rust use ramhorns::{Template, Content}; use std::io::Write; #[derive(Content)] struct Page { title: String, content: String, } fn main() -> Result<(), Box> { let source = "{{title}}{{content}}"; let tpl = Template::new(source)?; let page = Page { title: "My Page".to_string(), content: "Welcome to my website!".to_string(), }; // Render to a Vec buffer let mut buffer = Vec::new(); tpl.render_to_writer(&mut buffer, &page)?; // Or render directly to stdout let mut stdout = std::io::stdout(); tpl.render_to_writer(&mut stdout, &page)?; Ok(()) } ``` -------------------------------- ### Render Template with Tuple Struct Content (Numeric Accessors) Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Renders a template using tuple structs as content. Fields in tuple structs can be accessed using their numeric index (e.g., `{{0}}`, `{{1}}`). ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Point(f64, f64, f64); #[derive(Content)] struct Color(u8, u8, u8); fn main() -> Result<(), ramhorns::Error> { let point_tpl = Template::new("Point: ({{0}}, {{1}}, {{2}})")?; let point = Point(1.5, 2.5, 3.5); println!("{}", point_tpl.render(&point)); // Output: "Point: (1.5, 2.5, 3.5)" let color_tpl = Template::new("RGB: {{0}}, {{1}}, {{2}}")?; let color = Color(255, 128, 0); println!("{}", color_tpl.render(&color)); // Output: "RGB: 255, 128, 0" Ok(()) } ``` -------------------------------- ### Render Template to String Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Renders a template into a String, automatically escaping HTML special characters. Supports nested sections and iterative rendering. ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Blog<'a> { title: String, posts: Vec>, } #[derive(Content)] struct Post<'a> { title: &'a str, teaser: &'a str, } fn main() -> Result<(), ramhorns::Error> { let source = "

{{title}}

\ {{#posts}}

{{title}}

{{teaser}}

{{/posts}}\ {{^posts}}

No posts yet :(

{{/posts}}"; let tpl = Template::new(source)?; let rendered = tpl.render(&Blog { title: "My Awesome Blog!".to_string(), posts: vec![ Post { title: "How I tried Ramhorns and found love", teaser: "This can happen to you too", }, Post { title: "Rust is kinda awesome", teaser: "Yes, even the borrow checker!", }, ], }); // Output with nested sections rendered println!("{}", rendered); Ok(()) } ``` -------------------------------- ### Sections and Inverse Sections for Conditional Rendering Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Sections {{#name}}...{{/name}} render content when the value is truthy. Inverse sections {{^name}}...{{/name}} render when the value is falsy. Useful for displaying content based on boolean flags or collection emptiness. ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Page<'a> { show_banner: bool, user_name: &'a str, items: Vec>, } #[derive(Content)] struct Item<'a> { name: &'a str, price: f64, } fn main() -> Result<(), ramhorns::Error> { let source = r#" {{#show_banner}}{{/show_banner}} Hello {{user_name}}{{^user_name}}Guest{{/user_name}}! {{#items}}
{{name}}: ${{price}}
{{/items}} {{^items}}

No items in cart

{{/items}} "#; let tpl = Template::new(source)?; // With items let page_with_items = Page { show_banner: true, user_name: "Alice", items: vec![ Item { name: "Widget", price: 9.99 }, Item { name: "Gadget", price: 19.99 }, ], }; println!("{}", tpl.render(&page_with_items)); // Empty state let empty_page = Page { show_banner: false, user_name: "", items: vec![], }; println!("{}", tpl.render(&empty_page)); // Shows "Hello Guest!" and "No items in cart" Ok(()) } ``` -------------------------------- ### Lazy Template Loading Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Creates a template collection that loads templates on-demand when accessed via `from_file()`. This is useful for large template directories where not all templates are needed immediately. Templates are cached after the first load. ```rust use ramhorns::{Ramhorns, Content}; #[derive(Content)] struct Data { message: String, } fn main() -> Result<(), ramhorns::Error> { // Create lazy loader - doesn't read any files yet let mut tpls: Ramhorns = Ramhorns::lazy("./templates")?; // Template is loaded and cached on first access let tpl = tpls.from_file("greeting.html")?; let data = Data { message: "Hello!".to_string(), }; println!("{}", tpl.render(&data)); // Subsequent calls return cached template let tpl_cached = tpls.from_file("greeting.html")?; Ok(()) } ``` -------------------------------- ### Add Ramhorns Dependency to Cargo.toml Source: https://github.com/maciejhirsz/ramhorns/blob/master/README.md Include Ramhorns as a dependency in your project's Cargo.toml file to use its templating features. ```toml [dependencies] ramhorns = "0.5" ``` -------------------------------- ### Derive Content Macro for Structs Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Automatically implements the Content trait for structs. Supports field attributes for renaming, skipping, flattening, and markdown rendering. Ensure fields are accessible for template rendering. ```rust use ramhorns::{Template, Content}; #[derive(Content)] #[ramhorns(rename_all = "camelCase")] // Rename all fields to camelCase struct User<'a> { first_name: &'a str, // Accessible as {{firstName}} last_name: &'a str, // Accessible as {{lastName}} #[ramhorns(rename = "mail")] // Custom rename email: &'a str, // Accessible as {{mail}} #[ramhorns(skip)] // Skip this field entirely password: &'a str, } #[derive(Content)] struct Article<'a> { title: &'a str, #[ramhorns(md)] // Render as CommonMark markdown body: &'a str, } #[derive(Content)] struct Page<'a> { name: &'a str, #[ramhorns(flatten)] // Flatten nested struct fields metadata: Metadata<'a>, } #[derive(Content)] struct Metadata<'a> { author: &'a str, date: &'a str, } fn main() -> Result<(), ramhorns::Error> { // Using renamed fields let tpl = Template::new("{{firstName}} {{lastName}} <{{mail}}>")?; let user = User { first_name: "John", last_name: "Doe", email: "john@example.com", password: "secret", }; println!("{}", tpl.render(&user)); // Output: "John Doe " // Using markdown rendering let tpl = Template::new("

{{title}}

{{body}}
")?; let article = Article { title: "Hello", body: "This is **bold** text", }; println!("{}", tpl.render(&article)); // Output: "

Hello

This is bold text

\n
" // Using flattened fields let tpl = Template::new("{{name}} by {{author}} on {{date}}")?; let page = Page { name: "My Article", metadata: Metadata { author: "Jane", date: "2024-01-01", }, }; println!("{}", tpl.render(&page)); // Output: "My Article by Jane on 2024-01-01" Ok(()) } ``` -------------------------------- ### Handle Template Errors Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Use pattern matching on the Error enum to handle specific issues like unclosed sections, unclosed tags, or missing template files. ```rust use ramhorns::{Template, Ramhorns, Error}; fn main() { // Unclosed section error match Template::new("{{#section}}content") { Ok(_) => println!("Template parsed"), Err(Error::UnclosedSection(name)) => { println!("Error: Section '{}' was not closed", name); } Err(e) => println!("Other error: {}", e), } // Unclosed tag error match Template::new("Hello {{name") { Ok(_) => println!("Template parsed"), Err(Error::UnclosedTag) => { println!("Error: Tag was not closed with }}"); } Err(e) => println!("Other error: {}", e), } // File not found error let mut tpls: Ramhorns = Ramhorns::lazy("./templates").unwrap(); match tpls.from_file("nonexistent.html") { Ok(_) => println!("Template loaded"), Err(Error::NotFound(name)) => { println!("Error: Template '{}' not found", name); } Err(e) => println!("Other error: {}", e), } } ``` -------------------------------- ### Apply Custom Callbacks in Ramhorns Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Use the #[ramhorns(callback = function)] attribute to transform field values during rendering. The callback function must accept a string and an encoder. ```rust use ramhorns::{Template, Content}; use ramhorns::encoding::Encoder; // Custom callback that doubles the text fn double(s: &str, enc: &mut E) -> Result<(), E::Error> where E: Encoder, { enc.write_escaped(s)?; enc.write_escaped(" + ")?; enc.write_escaped(s) } // Custom callback that uppercases text fn uppercase(s: &str, enc: &mut E) -> Result<(), E::Error> where E: Encoder, { enc.write_escaped(&s.to_uppercase()) } #[derive(Content)] struct Message<'a> { #[ramhorns(callback = double)] repeated: &'a str, #[ramhorns(callback = uppercase)] loud: &'a str, } fn main() -> Result<(), ramhorns::Error> { let tpl = Template::new("{{repeated}} | {{loud}}")?; let msg = Message { repeated: "echo", loud: "hello world", }; println!("{}", tpl.render(&msg)); // Output: "echo + echo | HELLO WORLD" Ok(()) } ``` -------------------------------- ### HTML Escaping and Unescaping Variables Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Variables are HTML-escaped by default using {{variable}}. Use {{{variable}}} or {{&variable}} for unescaped output when rendering trusted HTML content. Ensure the content is safe before unescaping. ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Page { content: String, } fn main() -> Result<(), ramhorns::Error> { let tpl = Template::new( "Escaped: {{content}}\nUnescaped: {{{content}}}\nAlso unescaped: {{&content}}" )?; let page = Page { content: "Bold & \"quoted\"".to_string(), }; let rendered = tpl.render(&page); println!("{}", rendered); // Output: // Escaped: <strong>Bold</strong> & "quoted" // Unescaped: Bold & "quoted" // Also unescaped: Bold & "quoted" Ok(()) } ``` -------------------------------- ### Access Parent Context in Nested Sections Source: https://context7.com/maciejhirsz/ramhorns/llms.txt Nested sections automatically inherit access to fields from parent contexts, allowing templates to reference outer scope data. ```rust use ramhorns::{Template, Content}; #[derive(Content)] struct Company<'a> { name: &'a str, departments: Vec>, } #[derive(Content)] struct Department<'a> { dept_name: &'a str, employees: Vec>, } #[derive(Content)] struct Employee<'a> { emp_name: &'a str, } fn main() -> Result<(), ramhorns::Error> { let source = r#" Company: {{name}} {{#departments}} Department: {{dept_name}} {{#employees}} - {{emp_name}} works in {{dept_name}} at {{name}} {{/employees}} {{/departments}} "#; let tpl = Template::new(source)?; let company = Company { name: "Acme Corp", departments: vec![ Department { dept_name: "Engineering", employees: vec![ Employee { emp_name: "Alice" }, Employee { emp_name: "Bob" }, ], }, Department { dept_name: "Sales", employees: vec![ Employee { emp_name: "Charlie" }, ], }, ], }; println!("{}", tpl.render(&company)); // Employees can access both their department name and company name Ok(()) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.