9 releases

0.1.3-alpha.7 Oct 10, 2024
0.1.3-alpha.5 Oct 9, 2024
0.1.1 Sep 30, 2024
0.1.0-alpha.1 Aug 19, 2024

#137 in Template engine

Download history 148/week @ 2024-08-19 16/week @ 2024-08-26 16/week @ 2024-09-16 16/week @ 2024-09-23 487/week @ 2024-09-30 719/week @ 2024-10-07 51/week @ 2024-10-14

1,266 downloads per month

MIT/Apache

17KB
137 lines

origami-engine

Origami Engine is a templating engine focused on modularity, designed for efficient HTML generation with powerful macros.

Features

  • Focused on modularity for easy extensibility
  • Support for expressions, conditionals, loops, and match expressions

Basic Example

use origami_engine::comp;

comp! {
    greeting =>
    div {
        "Hello, World!"
    }
}

let html = greeting!();
assert_eq!(html.0, "<div>Hello, World!</div>");

Documentation

For comprehensive documentation and usage instructions, please visit docs.rs.

License

This project is licensed under the MIT License or the Apache License 2.0. See the LICENSE-MIT and LICENSE-APACHE files for details.


lib.rs:

Origami Engine

A Rust templating engine that allows for rendering HTML elements and building reusable components.

Props Passing

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.

use origami_engine::comp;

// Define a button component that takes props
comp! {
    button_component(attr, label) =>
    button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600" @attr; {
        @label;
    }
}

// Define the homepage component
comp! {
    home =>
    div {
        h1 { "Welcome to the Homepage!" }
        // Use the button_component with props
        call button_component { attr { onclick="alert('clicked')" }, label { "Click Me" } }
    }
}

let html = home!();

assert_eq!(
    html.0,
    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>"#
);

// Define the about page component
comp! {
    about =>
    div {
        h1 { "About Us" }
        p { "We are committed to delivering quality service." }
        // Use the button_component with props
        call button_component { attr { onclick="alert('clicked learn more')" }, label { "Learn More" } }
    }
}

let html = about!();
assert_eq!(
    html.0,
    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>"#
);

Layout

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.

use origami_engine::comp;

// Define a layout component with a navigation bar, body, and footer
comp! {
    layout_component(content) =>
    // Navigation bar
    nav {
        ul {
            li { a { "Home" } }
            li { a { "About" } }
            li { a { "Contact" } }
        }
    }
    // Body placeholder for dynamic content
    main {
        @content;
    }
    // Footer
    footer {
        p { "© 2024 Your Company" }
    }
}

// Define the homepage component using the layout
comp! {
    home =>
    call layout_component {
        content {
            h1 { "Welcome to the Homepage!" }
            p { "This is the main content of the homepage." }
        }
    }
}

let html = home!(cap => 250); // It is recommended to provide `cap`, i.e., the maximum length of html
                             // to avoid unnecessary reallocations of strings
assert_eq!(
    html.0,
    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>"#
);

// Define the about page component using the layout
comp! {
    about =>
    call layout_component {
        content {
            h1 { "About Us" }
            p { "We are committed to delivering quality service." }
        }
    }
}

let html = about!(cap => 250); // It is recommended to provide `cap`, i.e., the maximum length of html
                             // to avoid unnecessary reallocations of strings
assert_eq!(
    html.0,
    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>"#
);

Escape and Noescape

You can use escape and noescape to control HTML escaping behavior in the template (html_escape is feature is required):

#[cfg(feature = "html_escape")]
{
    use origami_engine::comp;

    comp! {
        foo =>
        div noescape {
            div { "<div>Unsafe HTML</div>" } // Inherited, this will not be escaped
            div escape {
                "<div>Safe HTML</div>" // This will be escaped
            }
        }
    }

    let html = foo!();
    assert_eq!(html.0, "<div><div><div>Unsafe HTML</div></div><div>&lt;div&gt;Safe HTML&lt;/div&gt;</div></div>");
}

You can also use noescape or escape with conditional rendering:

#[cfg(feature = "html_escape")]
{
    use origami_engine::comp;

    let text = "bar";

    comp! {
        foo =>
        div noescape {
            if text == "foo"; noescape {
                "<div>Unsafe HTML</div>"
            } else if text == "bar"; escape {
                "<div>Safe HTML</div>"
            } else noescape {
                "<div>Default HTML</div>"
            }
        }
    }

    let html = foo!();
    assert_eq!(html.0, "<div>&lt;div&gt;Safe HTML&lt;/div&gt;</div>");
}

Or with expressions:

#[cfg(feature = "html_escape")]
{
    use origami_engine::comp;

    let text = "<div>foo</div>";
    comp! {
        foo =>
        div { @text;! }
    }

    let html = foo!();
    assert_eq!(html.0, "<div><div>foo</div></div>");
}

Or with literals:

#[cfg(feature = "html_escape")]
{
    use origami_engine::comp;

    comp! {
        foo =>
        div {
            "<div>foo</div>"!
        }
    }

    let html = foo!();
    assert_eq!(html.0, "<div><div>foo</div></div>");
}

Or match expressions:

#[cfg(feature = "html_escape")]
{
    use origami_engine::comp;

    let text = "foo";
    comp! {
        foo =>
        div {
            match text; noescape {
                "foo" => {
                    "<div>foo</div>" // Inherited, this will not be escaped
                },
                _ => escape {
                    "<div>foo</div>" // This will be escaped
                }
            }
        }
    }

    let html = foo!();
    assert_eq!(html.0, "<div><div>foo</div></div>");
}

Dependencies

~1.2–9.5MB
~89K SLoC