Git Product home page Git Product logo

ructe's Introduction

Rust Compiled Templates — ructe

This is my attempt at writing a HTML template system for Rust. Some inspiration comes from the scala template system used in play 2, as well as plain old jsp.

Crate docs CI

Design criteria

  • As many errors as possible should be caught at compile-time.
  • A compiled binary should include all the template code it needs, no need to read template files at runtime.
  • Compilation may take time, running should be fast.
  • Writing templates should be almost as easy as writing html.
  • The template language should be as expressive as possible.
  • It should be possible to write templates for any text-like format, not only html.
  • Any value that implements the Display trait should be outputable.
  • By default, all values should be html-escaped. There should be an easy but explicit way to output preformatted html.

Current status

Ructes is in a rather early stage, but does work; templates can be transpiled to rust functions, which are then compiled and can be called from rust code.

Template format

A template consists of three basic parts: First a preamble of use statements, each prepended by an @ sign. Secondly a declaration of the parameters the template takes. And third, the template body.

The full syntax is described in the documentation. Some examples can be seen in examples/simple/templates. A template may look something like this:

@use any::rust::Type;
@use super::statics::style_css;

@(name: &str, items: &[Type])

<html>
   <head>
     <title>@name</title>
     <link rel="stylesheet" href="/static/@style_css.name" type="text/css"/>
   </head>
   <body>
     <h1>@name</h1>
     <dl>
     @for item in items {
       <dt>@item.title()</dt>
       <dd>@item.description()</dd>
     }
     </dl>
   <body>
</html>

How to use ructe

Ructe compiles your templates to rust code that should be compiled with your other rust code, so it needs to be called before compiling, as described in the documentation. There are also examples, both for ructe itself and its futures and for using it with the web frameworks axum, actix-web, gotham, iron, nickel, tide, and warp. There is also a separate example of using ructe with warp and diesel.

ructe's People

Contributors

abd0-omar avatar bearfrieze avatar dkotrada avatar eroc33 avatar ibraheemdev avatar jo-so avatar kaj avatar klosspeter avatar kornelski avatar noughmad avatar prabirshrestha avatar rustafariandev avatar vbrandl avatar wezm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ructe's Issues

if statement compare values

Currently imposible compare values at if statment.
For example:

@(myval: &str)
@if myval == "test" {...}
error exception
``

@(myval: i32)
@if myval > 10 {...}
error exception
``

valid:

@(myval: bool)
@if myval {...}

or

@(myval: &MyStruct)
@if myval.my_method() {...}

It is the load on the code and in general it is not convenient.

Do you want to improve?

Omit element

I often want to conditionally wrap content in an element, e.g. write <a>@foo</a> or @foo depending on whether I have a URL to link to.

Current solutions aren't great. I either have to repeat the code that generates the content:

@if let Some(url) = url {
   <a href="@url">@label</a>
} else {
   @label
}

or repeat the condition:

@if let Some(url) = url {
   <a href="@url">
}
@label
@if url.is_some() {
   </a>
}

which could lead to invalid HTML generated if the second condition wans't exactly matching the first one.

TAL has a feature for this. It'd be something like this:

<a tal:omit-tag="url.is_none()" href="@url">@label</a>

If the condition in the opening tag is false, the closing tag is also removed.

How do I call async functions from templates?

I'm using actix-web together with sqlx and based on some if-statements inside my templates I want to fetch additional data from the database using some async struct methods.

What is the recommended approach for this (if it's even possible)?

Template direcrory structure

@kaj Thank you for really awesome Template Engine.
I provide several tests and it is extremely fast, compile-time template checking.
But it is inconvenient thing - I can't structuring my templates.
Fro example:

templates/
    admin/
        main.rs.html
    layouts/
        _header.rs.html
        main.rs.html

Are you planning implement it?

"" is not accepted as valid Rust

@if foo != "" {}

doesn't compile in the templates, due to "". Similarly, @foo("") is compiled as foo.to_html()

For if I suggest not trying to understand Rust grammar, but only shallow token-level syntax. @if *any number of Rust tokens as long as braces/parens match* { … }. Fortunately Rust gives such guarantee, so no matter how complex the code is, it's enough to skip over pairs of braces/parens and string/char literals that could contain them.

static file cant start with a number

In the function add_static, the variable rust_name start with the name of the file, so it breaks.

It's not a big deal, but i thing it's worth mentioning.

Support for 2018 edition?

Hi, I recently tried using ructe in a 2018 edition project. Unfortunately the templates are generated with the old style absolute paths like:

error[E0432]: unresolved import `templates`
 --> /opt/jezila/ructe/examples/simple/target/debug/build/simple-3b5f8e3dc18a428a/out/templates/template_list.rs:4:7
  |
use ::templates::{Html,ToHtml};
  |       ^^^^^^^^^ Did you mean `crate::templates`?

Is there a way to tell ructe to generate with new style paths (that start with crate)? Please excuse my ignorance if there is an obvious answer - I'm pretty new with Rust...

Love the library btw!

New bindings (or arbitrary code)

Hi.

First of all kudos on making the only templating library I can actually use for productive work in the whole rust ecosystem.

Would you be interested in merging a pull request adding arbitrary code evaluation and bindings?

For the usual case where one needs a shorthand for a nested value, or bind the result of a helper to then inspect it (ie, grouping a list in groups of 3).

I can settle for an idiomatic best practice too, any suggestions there?

Thanks again, awesome library.

Cannot use turbofish syntax in template

The following template code does not work:

<p>@books.into_iter().map(|b| b.title).collect::<Vec<String>>().join(", ")</p>

and results in the following error

  --> C:\Users\oskar\git\books-rs\target\debug\build\books-rs-44f2c21020dbf42e\out\templates\template_books_html.rs:12:46
   |
12 | books.into_iter().map(|b| b.title).collect.to_html(&mut _ructe_out_)?;
   |                                    ^^^^^^^ help: use parentheses to call the method: `collect()`

Parse error with inline CSS

With the following input:

@()
<!doctype html>
<html>
<head>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>
<body>
</body>
</html>

Ructe gives:

warning: Template parse error in "templates/test.rs.html":
warning:    6:        * {
warning:                ^ Error in expression starting here:

Not informative error messages

Error message completely not informative.

Template:

@use admin::handlers::main::HeaderData;
@(h: &HeaderData)
@if h.is_active("main") == true { active }

Error message:

   Compiling nickel-cms v0.3.0 (file:///home/evgeny/rs/nickel-cms)
warning: Template parse error in "/home/evgeny/rs/nickel-cms/templates/admin/layout/sidebar.rs.html": End of file
error[E0432]: unresolved import `templates::admin::layout::sidebar`
 --> /home/evgeny/rs/nickel-cms/target/debug/build/nickel-cms-ce85cbf7f3e4b8e4/out/templates/admin/layout/template_header.rs:4:5
  |
4 | use templates::admin::layout::sidebar;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `sidebar` in `templates::admin::layout`

error: aborting due to previous error

error: Could not compile `nickel-cms`

or like that:

warning: Failed to parse template "/home/evgeny/rs/nickel-cms/templates/admin/helpers/active.rs.html": Size(114) needed

It's impossible debugging with these messages.
I suggest strongly improve error reporting messages.

Unable to use || && in @if

Template containing:

@if true || false {
}

does not compile:

The following warnings were emitted during compilation:

warning: Template parse error in "templates/test.rs.html":
warning:    3:@if true || false {
warning:      ^ Error in expression starting here:
warning:    3:@if true || false {
warning:          ^ Error in conditional expression:
warning:    3:@if true || false {
warning:               ^ Expected "{"
warning:    3:@if true || false {
warning:               ^ Char

Escape dots - end of expression

I've been migrating from Java to Rust with ructe, and got into a problem which I don't now how to solve. So I have a number of SQL temples like this one, and they cannot be compiled:

ALTER SEQUENCE @(model.schema).@(model.get_sequence_name())
	OWNED BY @(model.schema).@(model.name).id;

I got:

error[E0609]: no field `id` on type `std::string::String`
  --> D:\Development\coderator-rs\target\debug\build\coderator-94163fea6e350a38\out\templates\template_create_html.rs:24:12
   |
24 | model.name.id.to_html(&mut out)?;
   |            ^^ unknown field

error: aborting due to previous error

For more information about this error, try `rustc --explain E0609`.
error: could not compile `coderator`.

To learn more, run the command again with --verbose.

Please, consider to implement rocker-like eval expressions from the beast Java template engine I've ever seen. Everything outside @(...) should be treated as a template text, not as an expression.

I know that there already was #2 but I still don't know to make my templates work.

Join Vector<Html<T>> with separator

How can I join a vector of html strings with a custom separator? With for exampe Vec<String> I would use join but this isn't available for Html. This is what I've tried:

   |
16 | (users.into_iter().map(|u| Html(format!("<a href=\"example.com/{}\">{}</a>", u.name, u.name))).collect::<Vec<Html<String>>>().join(",")).to_html(&mut _ructe_out_)?;
   |                                                                                                                               ^^^^ method not found in `std::vec::Vec<templates::Html<std::string::String>>`
   |
   = note: the method `join` exists but the following trait bounds were not satisfied:
           `<[templates::Html<std::string::String>] as std::slice::Join<_>>::Output = _`

Whitespace control

I have a couple of places in HTML where I want to prevent any extra whitespace in the generated output:

  • When generating multiple inline or inline-block elements that shouldn't have gaps created by spaces.
  • When inserting commas or periods after elements, since a space before punctuation marks is generally inappropriate typographically.

Currently I'm just writing templates without spaces in them, but this isn't very convenient, since it requires use of long lines, and careful avoidance of any spaces.

@if let Some(url) = url.author(a) {
    <a href="@url"><span>@a.name()</span></a>@if !last {,} else {.}
}
<span class="version">@d.dep.req()</span>@for f in d.dep.req_features() {<span class="with-feature"><wbr>+<span>@f</span></span>}

So ideally I'd like some kind of block/directive that allows me to write template source with line breaks and indentation as usual, but remove that whitespace from the output.

I'm not sure what the syntax for it should look like, but generally template languages have blocks that either ignore all whitespace around elements, or trim whitespace at beginning and end of every line.

Allow `else if` conditions

Can't compile this simple template:

@()

@if false {
1
} else if false {
2
} else {
3
}

It says:

warning: Template parse error in "templates\\test.rs.html":
warning:    5:} else if false {
warning:                      ^ Error in expression starting here:

If I remove the else if condition, then it will compile.

rustc 1.44.0 (49cae5576 2020-06-01)
cargo 1.44.0 (05d080faa 2020-05-06)
ructe 0.11.4

match enum struct variant defect?

I try to match the following enum with internal struct variants

pub enum ValidationError {
    LengthOutOfBounds {
        min: usize,
        max: usize,
    },
    InvalidName {
        invalid_chars: Vec<char>,
    },
    UniqueConstraintViolation,
}

with

@use crate::ValidationError::{self, *};

@(error: &ValidationError)

@match error {
    UniqueConstraintViolation => {
        This name is taken.
    }
    LengthOutOfBounds { min, max } => {
        This field must contain at min @min and at max @max characters.
    }
    InvalidName { invalid_chars } => {
        @format!("The name must not contain any of the following characters: {:?}", invalid_chars)
    }
}

The error message is:

warning: Template parse error in "template_src/validation_error_display_part.rs.html":
warning:    5:@match error {
warning:      ^ Error in expression starting here:
warning:    5:@match error {
warning:             ^ Error in match expression:
warning:    8:    }
warning:           ^ Error in match arm starting here:

this works:

@use crate::ValidationError::{self, *};

@(error: &ValidationError)

@match error {
    UniqueConstraintViolation(value) => {
        This name is taken.
    }
    _ => {
        other error
    }
}

In rust this works:

impl<'s> std::fmt::Display for ValidationError<'s> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        use ValidationError::*;
        match self {
            LengthOutOfBounds { min, max } => {
                write!(
                    f,
                    "This field must contain at min {} and at max {} characters.",
                    min, max
                )
            }
            InvalidName {
                invalid_chars,
            } => {
                write!(
                    f,
                    "The name must not contain any of the following characters: {:?}",
                    invalid_chars
                )
            }
            UniqueConstraintViolation => {
                write!(f, "This name is taken.")
            }
        }
    }
}

Am I doing something wrong? Is this a defect of the library?

I enjoy using ructe 🦀 thankyou for sharing 🙏

Writing raw html from a string

I've written some code to declare html form elements & produce html tags in a String. I would have written it in ructe but match isn't supported yet, and I don't know enough Rust yet to add match support to ructe myself.

It would be handy if I could write a string to the document without html entities being escaped. I tried to use the ToHtml trait but that doesn't seem to be exported from the crate...? and I couldn't find any other way to achieve this.

context aware escaping

You already mentioned that you need different escaping for different languages to template. However with the web it's way worse, because you have different languages in one file. Escaping is different for html code in an html file, than for css code in that same html file, javascript code in that same html file, a string constant in javascript code in that html file. Let the programmer (a typical web designer) choose their poison -- ehrm escaping function and they will make mistakes. This leads to exploits which are already out there in millions. Rust is supposed to be secure, right?

From a templating engine written in Rust in 2016 one expects no less than a safety guarantee, that all strings are well-escaped regarding their context. This is not as easy as it may sound. I see two ways to do this.

  1. While writing the template, have a parser running in the background, which keeps track, which context the writer is currently in. I believe, the Servo people should have something like that lying around. This would be a dynamically enforced guarantee.
  2. The AST of the template without the insertions is parsed at compile-time, and so for each insertion, the context is already known statically. The problem with that is, that you can't modify the AST anymore without telling ructe (e.g. no pasting of a pre-html-formatted article text), because it could invalidate ructe's assumption about the context we're supposed to be in. Or it could be allowed, if you write unsafe around it. Or the html strings could be parsed dynamically and checked for closedness in the given static context (i.e. the original context is restored after the string). Closedness results for commonly used strings (frequently-accessed article html code from a database) could be cached to avoid running the parser on every execution.

Sadly, of course the client's browser could use a non-standard parser and build up a completely different AST, in which case (s)he will not be protected. But in 2016, with all the web standards we have, this should be a very rare case.

What are your thoughts?

Support unparameterized templates

Currently, if you have a plain HTML file, eg:

<!doctype html>
<html>
<head>
</head>
<body>
</body>
</html>

Ructe gives an error:

warning: Template parse error in "templates/test.rs.html":
warning:    1:<!doctype html>
warning:      ^ expected '@('...')' template declaration.

It would be great to be able to have a plain HTML file "just work"

Build status on commits

For some reason, Travis stopped reporting back build status for commits and pull requests back in Mars.

The project still builds, but commits and pull requests on github are no longer marked with their build status.

Maybe I should use Github Actions for CI anyway?

[Feature Proposal] Utilize macros

It would be nice if templates could be rendered without using build scripts.
The current approach is not very bad, but it could be more ergonomic with macros.

The pro for using macros is that special build scripts would be unnecessary, and this crate would no longer need "special treatment" to set up. It might also be friendlier for development tools, but I am not entirely sure on this point.

The con is that, as far as I understand, this would require setting up another ructe-codegen crate that hosts the procedural macro(s). This might also require exposing some previously private apis in altered form.

For what an api using macros could look like, something like this comes to mind:

let mut buf = Vec::new();
render_template!("templates/hello_args_two.rs.html", &mut buf, 25, "prime", false);

At least naively implemented, this has the disadvantage that templates will need to be parsed once for every use of that template. Also having so many arguments is just not very pretty or readable.

Alternatively, something like

include_template!("templates/hello_args_two.rs.html");
// ...
fn do_template_stuff() {
    let mut buf = Vec::new();
    args_two_html(&mut buf, 25, "prime", false);
    // ...
}

might be worth considering even though it is imo less intuitive because the macro looks more like a function call than a function definition.

let hello_args = template!("templates/hello_args_two.rs.html");
let mut buf = Vec::new();
hello_args.render(&mut buf, 25, "prime", false);

where template! returns an instance of some struct generated from within the macro might also work.

As you can tell, this idea is not very fleshed out yet, but I believe it has a lot of potential in making the library easier to use, and I would love to hear ideas and opinions on the matter.

Generated out argument should use dyn or impl Write

With the Rust 2018 edition, using Ructe produces lots of warning: trait objects without an explicit 'dyn' are deprecated, one for every generated template function, because of the &mut Write argument. According to the Edition Guide, you should use &mut dyn Write for dynamic dispatch and &mut impl Write for static dispatch.

Further, I don't think dynamic dispatch is necessary here, so &mut impl Write should be preferred to avoid a vtable lookup on every template function call. But I'm not completely sure that this doesn't break some existing code. If it does break some compatibility guarantees, it would still be beneficial to provide a configuration option at generate-time.

In either case, I am willing to prepare a PR if such a change is welcome.

parse error with <style> tag

I decided to try ructe for a small web project.

This is a reduced sample that should suffice to reproduce the problem.

main.rs:

include!(concat!(env!("OUT_DIR"), "/templates.rs"));

fn main() {
    templates::style(&mut std::io::stdout()).unwrap();
}

style.rs.html:

@()

<html>
<head>
<style>
#content {
    color: red;
}
</style>
</head>

<body>
<div id="content">Content!</div>
</body>
</html>

This results in a compilation error:

error[E0425]: unresolved name `templates::style`
 --> src/main.rs:4:5
  |
4 |     templates::style(&mut std::io::stdout()).unwrap();
  |     ^^^^^^^^^^^^^^^^

A more informative message is found in target/debug/build/<project>/output:

cargo:warning=Template parse error Eof in "/path/to/project/ructe-issue-3/templates/style.rs.html": "{\n    color: red;\n}\n</style>\n</head>\n\n<body>\n<div id=\"content\">Content!</div>\n</body>\n</html>\n"

The templates compiles if I remove the content of the <style>-tag.

My guess would be that ructe is confused by the curly braces in CSS, as the error message reports an Eof parse error at the first {.

Template parse error with lambda and condition

Can't compile this simple template:

@()

@Option::None.unwrap_or_else(|| {
    let mut text = String::from("AB");

    if true {
        text.push('C');
    }

    return text;
})

It says:

warning: Template parse error in "templates\\test.rs.html":
warning:    2:@Option::None.unwrap_or_else(|| {
warning:                                      ^ Error in expression starting here:

If I remove the if condition, then it will compile.

rustc 1.39.0 (4560ea788 2019-11-04)
cargo 1.39.0 (1c6ec66d5 2019-09-30)
ructe 0.9.0

excess newline

there's an extra newline appended to the output of the template function. Is there a reason for this or can it be removed?

@()
Hello World!
use std::io::{self, Write};
#[cfg_attr(feature="cargo-clippy", allow(useless_attribute))]
#[allow(unused)]
use ::templates::{Html,ToHtml};

pub fn wireless(out: &mut Write)
-> io::Result<()> {
write!(out, "Hello World!\n\n")?;
Ok(())
}

end of expression

Currently ructe, inspired by twirl, uses a special character (@) to signal the start of an expression, but nothing special to signal the end of an expression. This is very nice when writing simple expressions surrounded by whitespace, tags, or anything else that doesn't look like a part of an expression, but it requires ructe to know the syntax of valid rust expressions and it is problematic if an expression should be immediately followed by something that looks like an expressions.

Should support for an optional — or required — end-of-expression marker be added? Maybe something like {=expression}? Otherwise, the expression parser will need to be improved. If I do and an end-of-expression marker, it will change the syntax of valid templates, so it should probably be decided one way or the other as soon as possible.

How about @…\n for simple stuff and @{…} for more complicated stuff (where could contain new lines only in the latter case)?

Support () in expressions

As per #2 ructe does not reliably find whole Rust expressions. A workaround for that could be supporting () as a special case. Because Rust guarantees all parenthesis are balanced in valid expressions, ructe won't need to parse full expressions, just tokenize (to skip string literals) and balance parens.

@if let Some(i) = Some(1) {
    @i+1 // prints 1+1
    @(i+1) // error, but should be supported
}

For basic arithmetic I'm working around this using .saturating_add(1), but it's a bit silly.

In some cases I also need more complex expressions, e.g. to convert Option<String> to Option<&str> an ugly .as_ref().map(|s| s.as_str()) is needed, but this expression is too complex for Ructe.

Improved support for inline script/styling

I noticed that i missed that I'm able to escape brackets inside of style/scripts in the previous version of this issue-ticket. This actually works pretty well for me. Might be an issue with lots of escaping with css in particular, but im not sure what could be a better solution.

Declaring reusable variables

Is there something similar to Django's with tag to store result of complex or expensive methods for use in the template multiple times?

Rocket Support

It would be great if at some point ructe directly supported Rocket. It is a really nice rust web engine that is growing in popularity.

html only?

why do the template files have to end in .html?

Compilation errors in 0.4.4

error[E0412]: cannot find type ErrorKind in this scope

ructe-0.4.4/src/errors.rs:8:29
|
8 | static ref ERR: ErrorKind = def_error($msg);
| ^^^^^^^^^ not found in this scope

ructe-0.4.4/src/templateexpression.rs:199:26
|
199 | return_error!(err_str!("Expected "{""), char!('{')),
| -------------------------- in this macro invocation
help: possible candidates are found in other modules, you can import them into scope
|
1 | use nom::ErrorKind;
|
1 | use std::io::ErrorKind;
|

no method named clone found for type templateexpression::template_block::ERR in the current scope
--> ructe-0.4.4/src/errors.rs:10:13
|
10 | ERR.clone()
| ^^^^^
|

ructe-0.4.4/src/templateexpression.rs:199:26
|
199 | return_error!(err_str!("Expected "{""), char!('{')),
| --------------------------
| |
| method clone not found for this
| in this macro invocation
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item clone, perhaps you need to implement it:
candidate #1: std::clone::Clone
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

comments in body broken since 29dac3d5b4f

Since 29dac3d »Minor parser improvement.« comments in the body do not work. An example for examples/simple/templates/comments.rs.html

@* A very simple template with no arguments.  This line is a comment. *@
@()
@* This comment hides <span>unused code</span>. *@
<!-- this is a real HTML comment, which gets send to the client -->
<p>This is visible</p>
@*
  Multi
  line
  comment
*@
warning: Template parse error in "/home/joerg/git/ructe/examples/simple/templates/comments.rs.html":                                                           
warning:    6:@*
warning:      ^ Error in expression starting here:
warning:    6:@*
warning:      ^ In expression starting here
warning:    6:@*
warning:      ^ Switch
warning:    6:@*
warning:        ^ Expected rust expression
warning:    6:@*
warning:        ^ Alt

After reverting the commit, it works.

diff --git i/src/templateexpression.rs w/src/templateexpression.rs
index 9c27fa6..4c8519c 100644
--- i/src/templateexpression.rs
+++ w/src/templateexpression.rs
@@ -180,8 +180,7 @@ named!(
                            alt!(tag!(":") | tag!("{") | tag!("}") | tag!("@") |
                                 terminated!(
                                     alt!(tag!("if") | tag!("for")),
-                                    tag!(" ")) |
-                                value!(&b""[..])))),
+                                    tag!(" "))))),
             Some(b":") => map!(
                 pair!(rust_name,
                       delimited!(tag!("("),
@@ -250,16 +249,14 @@ named!(
                     expr: expr.to_string(),
                     body,
                 }) |
-            Some(b"") => map!(
-                expression,
-                |expr| TemplateExpression::Expression{ expr: expr.to_string() }
-            ) |
             None => alt!(
                 map!(comment, |()| TemplateExpression::Comment) |
                 map!(map_res!(is_not!("@{}"), from_utf8),
                      |text| TemplateExpression::Text {
                          text: text.to_string()
-                     })
+                     }) |
+                map!(preceded!(tag!("@"), expression),
+                     |expr| TemplateExpression::Expression{ expr: expr.to_string() })
             )
     ))
 );

Using static files without hash suffixes?

How do I use static files without getting the hash suffixes? I want to create the hashes in my Webpack pipeline instead and have problems with source maps if Ructe also adds a suffix

Passing values multiple levels when calling other templates

I have the following scenario where i would like to pass a value through multiple templates. The example below doesn't work as I can't call the parent with msg in this case. I can get around this by using &format!("{}", msg), but it would be nice with just using the variable. Not sure if it's a good idea or not, what do you think?

page.rs.html

@use super::page_parent_html;
@()
<div>@:page_parent_html("hello")</div>

page_parent.rs.html

@use super::page_grand_parent_html;
@(msg: &str)
<div>@:page_grand_parent_html(msg)</div>

page_grand_parent.rs.html

@(msg: &str)
<div>@msg</div>

Update to warp's rejection system 0.2.0-alpha breaks ructe

Hi! The 0.2.0-alpha version of warp (currently unreleased) breaks ructe compilation with the warp feature enabled. Warp has changed the rejection system to require that custom rejections implement the Reject trait. Since io::Error and http::Error don't implement this trait I made a wrapper around them that you can look at here:

https://github.com/nocduro/ructe/commit/863490e4669aa07ecb766c1b5eb91f5faf7bc66e

I didn't make a pull request because I'm not sure if this is the best way to fix this. Do these function ever error? Are the errors ever consumed by the warp app somewhere? Maybe a unit struct could be used as the error to save passing around the new error enum.

And then for transitioning, maybe another feature for warp0.2? I'm not sure how this kind of stuff is usually handled.

Thanks for the great library!

Add tide support

https://github.com/http-rs/tide

I'm currently doing something like this.

pub async fn not_found(_ctx: Request<AppState>) -> tide::Result {
    let mut buf = Vec::new();
    templates::statuscode404(&mut buf)?;

    Ok(Response::new(StatusCode::NotFound)
        .body(Body::from(buf))
        .set_mime(mime::TEXT_HTML_UTF_8))
}

Some newline characters are being removed

I have this template:

@()
Hello!@for n in &vec![0, 1, 2] {@if true {

The @n number.}}

Good bye!

I expected it would produce:

Hello!

The 0 number.

The 1 number.

The 2 number.

Good bye!

But there's no two last newline characters right before "Good bye!":

Hello!

The 0 number.

The 1 number.

The 2 number.Good bye!

rustc 1.39.0 (4560ea788 2019-11-04)
cargo 1.39.0 (1c6ec66d5 2019-09-30)
ructe 0.9.0

Lifetime support

Using lifetime annotations (eg @(content: Vec<&'static foo>)) does not work:

warning:    3:@(data: &BaseData, news: Vec<&'static NewsEntry>)
warning:                                  ^ expected ',' or ')'.

How to declare reusable blocks

How to declare reusable blocks?

@display(link: &Link) {
<li>@link.url @link.visual</li>;
}

warning: Template parse error in "D:\Creating\dev\pinnaculum\src\modules\navigation\sidebar.rs.html":
warning: 6:@display(link: &Link) {
warning: ^ Error in expression starting here:
warning: 6:@display(link: &Link) {
warning: ^ In expression starting here
warning: 6:@display(link: &Link) {
warning: ^ Switch
warning: 6:@display(link: &Link) {
warning: ^ Alt

I plan to use it like this:

    @for(link in links) { @:display(link) }

Inspired by https://www.playframework.com/documentation/2.6.x/ScalaTemplates#declaring-reusable-blocks

escaped curly brace emitted twice

It seems that since 0.4.0, escaped curly braces are emitted twice:

@{{{
@}}}


Not sure if it's worth to open another issue: Is there a way to escape @? I had no luck with @@ so I'm sticking to the HTML entity code for now.

Make indentation more intuitive

It would be cool if this template might produce something like:

@()
begin
@for n in &vec![0, 1, 2] {
  @if true {
  test
  }
}
end
begin
  test
  test
  test
end

But to get the same output I have to write the template in this way, and it looks kinda dirty:

@()
begin@for n in &vec![0, 1, 2] {@if true {
  test}}
end

So the point is: if a line contains only start or end of an expression, and while spaces or tabs or line-break, then it will be skipped. It will allow to write more readable and intuitive templates.

More examples:

The first one looks better, doesn't it? :)

I hope you will take more attention to Rocker developer's experience, 'cause we need such a great tool in Rust too.

Template parse error with newline character

Can't compile this simple template:

@()
@("1\n2\n3")

It says:

warning: Template parse error in "templates\\test.rs.html":
warning:    2:@("1\n2\n3")
warning:      ^ Error in expression starting here:

How can I write a string with a new line character?

rustc 1.39.0 (4560ea788 2019-11-04)
cargo 1.39.0 (1c6ec66d5 2019-09-30)
ructe 0.9.0

Sometimes can't find sub templates

For some reason sub templates can't find.
For example I have templates:

templates/admin/pages/index.rs.html
templates/admin/pages/create.rs.html

compile ok.
Create new template:

templates/admin/pages/new.rs.html
@use admin::handlers::HeaderData;
@(h: &HeaderData)

and in code invoke it:
render(res, |o| templates::admin::pages::new(o, &header))

and I have got error:

   Compiling nickel-cms v0.3.0 (file:///home/evgeny/rs/nickel-cms)
error[E0425]: unresolved name `templates::admin::pages::new`
  --> src/admin/handlers/pages.rs:38:21
   |
38 |     render(res, |o| templates::admin::pages::new(o, &header))
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved name

error: aborting due to previous error

but, if at existed template I include:
@:new(h) and compile it - it compile ok. Then remove it. Compile project again - Template founded.
It's really offensive bug.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.