Unify Render* and Template* traits
This commit is contained in:
parent
e5a471d9ca
commit
4e2232f2de
29
README.md
29
README.md
|
@ -16,14 +16,14 @@ Simple, small, and extremely fast template engine for Rust
|
|||
|
||||
## ✨ Features
|
||||
|
||||
- Simple and intuitive syntax inspired by [EJS](https://ejs.co/)
|
||||
- Include another template file inside template
|
||||
- Built-in filters
|
||||
- Minimal dependencies (<15 crates in total)
|
||||
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
||||
- Better error message
|
||||
- Syntax highlighting support ([vscode](./syntax/vscode), [vim](./syntax/vim))
|
||||
- Works on Rust 1.60 or later
|
||||
- Simple and intuitive syntax inspired by [EJS](https://ejs.co/)
|
||||
- Include another template file inside template
|
||||
- Built-in filters
|
||||
- Minimal dependencies (<15 crates in total)
|
||||
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
||||
- Better error message
|
||||
- Syntax highlighting support ([vscode](./syntax/vscode), [vim](./syntax/vim))
|
||||
- Works on Rust 1.60 or later
|
||||
|
||||
## 🐟 Example
|
||||
|
||||
|
@ -49,9 +49,9 @@ Template file (templates/hello.stpl):
|
|||
Code:
|
||||
|
||||
```rust
|
||||
use sailfish::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "hello.stpl")]
|
||||
struct HelloTemplate {
|
||||
messages: Vec<String>
|
||||
|
@ -69,14 +69,14 @@ You can find more examples in [examples](./examples) directory.
|
|||
|
||||
## 🐾 Roadmap
|
||||
|
||||
- `Template` trait ([RFC](https://github.com/rust-sailfish/sailfish/issues/3))
|
||||
- Template inheritance (block, partials, etc.)
|
||||
- `Render` derive macro ([RFC](https://github.com/rust-sailfish/sailfish/issues/3))
|
||||
- Template inheritance (block, partials, etc.)
|
||||
|
||||
## 👤 Author
|
||||
|
||||
🇯🇵 **Ryohei Machida**
|
||||
|
||||
* GitHub: [@Kogia-sima](https://github.com/Kogia-sima)
|
||||
- GitHub: [@Kogia-sima](https://github.com/Kogia-sima)
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
|
@ -96,5 +96,6 @@ Copyright © 2020 [Ryohei Machida](https://github.com/Kogia-sima).
|
|||
|
||||
This project is [MIT](https://github.com/rust-sailfish/sailfish/blob/master/LICENSE) licensed.
|
||||
|
||||
***
|
||||
---
|
||||
|
||||
_This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Create a new directory named `templates` in the same directory as `Cargo.toml`. Copy the following contents and paste it to a new file named `templates/hello.stpl`.
|
||||
|
||||
``` rhtml
|
||||
```rhtml
|
||||
<html>
|
||||
<body>
|
||||
<% for msg in &messages { %>
|
||||
|
@ -29,13 +29,13 @@ templates/
|
|||
<ol><li>Import the sailfish crates:</li></ol>
|
||||
|
||||
```rust
|
||||
use sailfish::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
```
|
||||
|
||||
<ol start="2"><li>Define the template struct to be rendered:</li></ol>
|
||||
|
||||
```rust
|
||||
#[derive(TemplateOnce)] // automatically implement `TemplateOnce` trait
|
||||
#[derive(RenderOnce)] // automatically implement `TemplateOnce` trait
|
||||
#[template(path = "hello.stpl")] // specify the path to template
|
||||
struct HelloTemplate {
|
||||
// data to be passed to the template
|
||||
|
|
|
@ -8,19 +8,19 @@ This documentation mainly focuses on concepts of the library, general usage, and
|
|||
|
||||
There are many libraries for template rendering in Rust. Among those libraries, sailfish aims at **rapid development** and **rapid rendering**. Sailfish has many features that other libraries might not support.
|
||||
|
||||
- Write a Rust code directly inside templates, supporting many Rust syntax (struct definition, closure, macro invocation, etc.)
|
||||
- [Built-in filters](https://docs.rs/sailfish/latest/sailfish/runtime/filter/index.html)
|
||||
- Minimal dependencies (<15 crates in total)
|
||||
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
||||
- Template rendering is always type-safe because templates are statically compiled.
|
||||
- Syntax highlighting ([vscode](http://github.com/rust-sailfish/sailfish/blob/master/syntax/vscode), [vim](http://github.com/rust-sailfish/sailfish/blob/master/syntax/vim))
|
||||
- Write a Rust code directly inside templates, supporting many Rust syntax (struct definition, closure, macro invocation, etc.)
|
||||
- [Built-in filters](https://docs.rs/sailfish/latest/sailfish/runtime/filter/index.html)
|
||||
- Minimal dependencies (<15 crates in total)
|
||||
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
||||
- Template rendering is always type-safe because templates are statically compiled.
|
||||
- Syntax highlighting ([vscode](http://github.com/rust-sailfish/sailfish/blob/master/syntax/vscode), [vim](http://github.com/rust-sailfish/sailfish/blob/master/syntax/vim))
|
||||
|
||||
## Upcoming features
|
||||
|
||||
Since sailfish is on early stage of development, there are many upcoming features that is not supported yet. You can find many [RFC](https://github.com/rust-sailfish/sailfish/issues?q=is%3Aissue+is%3Aopen+label%3A%22Status%3A+RFC%22)s in my repository. These RFC include:
|
||||
|
||||
- `Template` trait (which does not consume itself)
|
||||
- Template inheritance (block, partials, etc.)
|
||||
- `Render` derive macro (which does not consume itself)
|
||||
- Template inheritance (block, partials, etc.)
|
||||
|
||||
If you have any idea about them or want to implement that feature, please send a comment on the issue!
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
You can control the rendering behaviour via `template` attribute.
|
||||
|
||||
``` rust
|
||||
#[derive(TemplateOnce)]
|
||||
```rust
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "template.stpl", escape = false)]
|
||||
struct TemplateStruct {
|
||||
...
|
||||
|
@ -14,15 +14,15 @@ struct TemplateStruct {
|
|||
|
||||
`template` attribute accepts the following options.
|
||||
|
||||
- `path`: path to template file. This options is always required.
|
||||
- `escape`: Enable HTML escaping (default: `true`)
|
||||
- `delimiter`: Replace the '%' character used for the tag delimiter (default: '%')
|
||||
- `rm_whitespace`: try to strip whitespaces as much as possible without collapsing HTML structure (default: `false`). This option might not work correctly if your templates have inline `script` tag.
|
||||
- `path`: path to template file. This options is always required.
|
||||
- `escape`: Enable HTML escaping (default: `true`)
|
||||
- `delimiter`: Replace the '%' character used for the tag delimiter (default: '%')
|
||||
- `rm_whitespace`: try to strip whitespaces as much as possible without collapsing HTML structure (default: `false`). This option might not work correctly if your templates have inline `script` tag.
|
||||
|
||||
You can split the options into multiple `template` attributes.
|
||||
|
||||
``` rust
|
||||
#[derive(TemplateOnce)]
|
||||
```rust
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "template.stpl")]
|
||||
#[template(delimiter = '?')]
|
||||
#[template(rm_whitespace = true)]
|
||||
|
@ -36,10 +36,10 @@ struct TemplateStruct {
|
|||
Sailfish allows global and local configuration in a file named `sailfish.toml`. Sailfish looks for this file in same directory as `Cargo.toml` and all parent directories.
|
||||
If, for example, `Cargo.toml` exists in `/foo/bar/baz` directory, then the following configuration files would be scanned in this order.
|
||||
|
||||
- `/foo/bar/baz/sailfish.toml`
|
||||
- `/foo/bar/sailfish.toml`
|
||||
- `/foo/sailfish.toml`
|
||||
- `/sailfish.toml`
|
||||
- `/foo/bar/baz/sailfish.toml`
|
||||
- `/foo/bar/sailfish.toml`
|
||||
- `/foo/sailfish.toml`
|
||||
- `/sailfish.toml`
|
||||
|
||||
If a key is specified in multiple configuration files, the value in the deeper directory takes precedence over ancestor directories.
|
||||
|
||||
|
@ -49,7 +49,7 @@ If a key is specified in both configuration file and derive options, then the va
|
|||
|
||||
Configuration files are written in the TOML 0.5 format. Here is the default configuration:
|
||||
|
||||
``` toml
|
||||
```toml
|
||||
template_dirs = ["templates"]
|
||||
escape = true
|
||||
delimiter = "%"
|
||||
|
|
|
@ -2,16 +2,15 @@
|
|||
|
||||
## Tags
|
||||
|
||||
- `<% %>`: Inline tag, you can write Rust code inside this tag
|
||||
- `<%= %>`: Evaluate the Rust expression and outputs the value into the template (HTML escaped)
|
||||
- `<%- %>`: Evaluate the Rust expression and outputs the unescaped value into the template
|
||||
- `<%+ %>`: Evaluate the Rust expression producing a `TemplateOnce` value, and render that value into the template
|
||||
- `<%# %>`: Comment tag
|
||||
- `<%%`: Outputs a literal '<%'
|
||||
- `<% %>`: Inline tag, you can write Rust code inside this tag
|
||||
- `<%= %>`: Evaluate the Rust expression and outputs the value into the template (HTML escaped)
|
||||
- `<%- %>`: Evaluate the Rust expression and outputs the unescaped value into the template
|
||||
- `<%# %>`: Comment tag
|
||||
- `<%%`: Outputs a literal '<%'
|
||||
|
||||
## Condition
|
||||
|
||||
``` rhtml
|
||||
```rhtml
|
||||
<% if messages.is_empty() { %>
|
||||
<div>No messages</div>
|
||||
<% } %>
|
||||
|
@ -19,7 +18,7 @@
|
|||
|
||||
## loop
|
||||
|
||||
``` rhtml
|
||||
```rhtml
|
||||
<% for (i, msg) in messages.iter().enumerate() { %>
|
||||
<div><%= i %>: <%= msg %></div>
|
||||
<% } %>
|
||||
|
@ -27,17 +26,17 @@
|
|||
|
||||
## Includes
|
||||
|
||||
``` rhtml
|
||||
```rhtml
|
||||
<% include!("path/to/template"); %>
|
||||
```
|
||||
|
||||
## Filters
|
||||
|
||||
``` rhtml
|
||||
```rhtml
|
||||
<%= message | upper %>
|
||||
```
|
||||
|
||||
``` rhtml
|
||||
```rhtml
|
||||
{
|
||||
"id": <%= id %>
|
||||
"comment": <%- comment | json %>
|
||||
|
|
|
@ -26,7 +26,7 @@ You can write Rust statement inside `<% %>` tag.
|
|||
```
|
||||
|
||||
!!! Note
|
||||
Make sure that you cannot omit braces, parenthesis, and semicolons.
|
||||
Make sure that you cannot omit braces, parenthesis, and semicolons.
|
||||
|
||||
Sailfish is smart enough to figure out where the code block ends, so you can even include `%>` inside Rust comments or string literals.
|
||||
|
||||
|
@ -57,11 +57,11 @@ If you need to simply render `<%` character, you can escape it, or use evaluatio
|
|||
|
||||
Although almost all Rust statement is supported, the following statements inside templates may cause a strange compilation error.
|
||||
|
||||
- Function/Macro definition that render some contents
|
||||
- `impl` item
|
||||
- Macro call which defines some local variable.
|
||||
- Macro call which behaviour depends on the path to source file
|
||||
- Generator expression (yield)
|
||||
- Function/Macro definition that render some contents
|
||||
- `impl` item
|
||||
- Macro call which defines some local variable.
|
||||
- Macro call which behaviour depends on the path to source file
|
||||
- Generator expression (yield)
|
||||
|
||||
## Evaluation block
|
||||
|
||||
|
@ -100,35 +100,8 @@ If you want to render the results without escaping, you can use `<%- %>` tag or
|
|||
```
|
||||
|
||||
!!! Note
|
||||
Evaluation block does not return any value, so you cannot use the block to pass the render result to another code block. The following code is invalid.
|
||||
Evaluation block does not return any value, so you cannot use the block to pass the render result to another code block. The following code is invalid.
|
||||
|
||||
``` rhtml
|
||||
<% let result = %><%= 1 %><% ; %>
|
||||
```
|
||||
|
||||
## Component block
|
||||
|
||||
Rust expression inside `<%+ %>` tag is evaluated and then rendered by
|
||||
calling its `render_once()` method. If the value does not have an
|
||||
appropriate method, a compile-time error will be reported.
|
||||
|
||||
This makes it easy to use types which are `TemplateOnce` as components
|
||||
which can be embedded into other templates.
|
||||
|
||||
=== "Template A"
|
||||
|
||||
``` rhtml
|
||||
<strong>A <%= val %></strong>
|
||||
```
|
||||
|
||||
=== "Template B"
|
||||
|
||||
``` rhtml
|
||||
B <%+ A { val: "example" } %>
|
||||
```
|
||||
|
||||
=== "Result"
|
||||
|
||||
``` text
|
||||
B <strong>A example</strong>
|
||||
```
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use actix_web::error::InternalError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
||||
use sailfish::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "actix.stpl")]
|
||||
struct Greet<'a> {
|
||||
name: &'a str,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "include.stpl")]
|
||||
struct Include {
|
||||
title: String,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "simple.stpl")]
|
||||
struct Simple {
|
||||
messages: Vec<String>,
|
||||
|
|
|
@ -56,7 +56,6 @@ impl Default for Parser {
|
|||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum TokenKind {
|
||||
NestedTemplateOnce,
|
||||
BufferedCode { escape: bool },
|
||||
Code,
|
||||
Comment,
|
||||
|
@ -162,10 +161,6 @@ impl<'a> ParseStream<'a> {
|
|||
token_kind = TokenKind::BufferedCode { escape: false };
|
||||
start += 1;
|
||||
}
|
||||
Some(b'+') => {
|
||||
token_kind = TokenKind::NestedTemplateOnce;
|
||||
start += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -409,7 +404,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn nested_render_once() {
|
||||
let src = r#"outer <%+ inner|upper %> outer"#;
|
||||
let src = r#"outer <%- inner | upper %> outer"#;
|
||||
let parser = Parser::default();
|
||||
let tokens = parser.parse(src).into_vec().unwrap();
|
||||
assert_eq!(
|
||||
|
@ -421,13 +416,13 @@ mod tests {
|
|||
kind: TokenKind::Text,
|
||||
},
|
||||
Token {
|
||||
content: "inner|upper",
|
||||
content: "inner | upper",
|
||||
offset: 10,
|
||||
kind: TokenKind::NestedTemplateOnce,
|
||||
kind: TokenKind::BufferedCode { escape: false },
|
||||
},
|
||||
Token {
|
||||
content: " outer",
|
||||
offset: 24,
|
||||
offset: 26,
|
||||
kind: TokenKind::Text,
|
||||
},
|
||||
]
|
||||
|
|
|
@ -342,30 +342,23 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
|||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
"You cannot derive `Template` or `TemplateOnce` for tuple struct",
|
||||
"You cannot derive `Render` or `RenderOnce` for tuple struct",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = strct.generics.split_for_impl();
|
||||
|
||||
// render_once method always results in the same code.
|
||||
// This method can be implemented in `sailfish` crate, but I found that performance
|
||||
// drops when the implementation is written in `sailfish` crate.
|
||||
let inline = if cfg!(feature = "perf-inline") {
|
||||
Some(quote!(#[inline]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let tokens = quote! {
|
||||
impl #impl_generics sailfish::TemplateOnce for #name #ty_generics #where_clause {
|
||||
fn render_once(self) -> sailfish::RenderResult {
|
||||
use sailfish::runtime::{Buffer, SizeHint};
|
||||
static SIZE_HINT: SizeHint = SizeHint::new();
|
||||
|
||||
let mut buf = Buffer::with_capacity(SIZE_HINT.get());
|
||||
self.render_once_to(&mut buf)?;
|
||||
SIZE_HINT.update(buf.len());
|
||||
|
||||
Ok(buf.into_string())
|
||||
}
|
||||
|
||||
fn render_once_to(self, __sf_buf: &mut sailfish::runtime::Buffer) -> std::result::Result<(), sailfish::runtime::RenderError> {
|
||||
impl #impl_generics sailfish::RenderOnce for #name #ty_generics #where_clause {
|
||||
#inline
|
||||
fn render_once(self, __sf_buf: &mut sailfish::runtime::Buffer) -> std::result::Result<(), sailfish::runtime::RenderError> {
|
||||
// This line is required for cargo to track child templates
|
||||
#include_bytes_seq;
|
||||
use sailfish::runtime as __sf_rt;
|
||||
|
@ -375,8 +368,6 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl #impl_generics sailfish::private::Sealed for #name #ty_generics #where_clause {}
|
||||
};
|
||||
|
||||
Ok(tokens)
|
||||
|
|
|
@ -214,11 +214,6 @@ impl SourceBuilder {
|
|||
TokenKind::BufferedCode { escape } => {
|
||||
self.write_buffered_code(&token, escape)?
|
||||
}
|
||||
TokenKind::NestedTemplateOnce => self.write_buffered_code_with_suffix(
|
||||
&token,
|
||||
false,
|
||||
".render_once()?",
|
||||
)?,
|
||||
TokenKind::Text => {
|
||||
// concatenate repeated text token
|
||||
let offset = token.offset();
|
||||
|
@ -384,7 +379,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn translate_nested_render_once() {
|
||||
let src = r#"outer <%+ inner %> outer"#;
|
||||
let src = r#"outer <%- inner %> outer"#;
|
||||
let lexer = Parser::new();
|
||||
let token_iter = lexer.parse(src);
|
||||
let mut ps = SourceBuilder {
|
||||
|
@ -400,13 +395,13 @@ mod tests {
|
|||
.ast
|
||||
.into_token_stream()
|
||||
.to_string(),
|
||||
r#"{ __sf_rt :: render_text (__sf_buf , "outer ") ; __sf_rt :: render (__sf_buf , inner . render_once () ?) ? ; __sf_rt :: render_text (__sf_buf , " outer") ; }"#
|
||||
r#"{ __sf_rt :: render_text (__sf_buf , "outer ") ; __sf_rt :: render (__sf_buf , inner) ? ; __sf_rt :: render_text (__sf_buf , " outer") ; }"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn translate_nested_render_once_with_filter() {
|
||||
let src = r#"outer <%+ inner|upper %> outer"#;
|
||||
let src = r#"outer <%- inner | upper %> outer"#;
|
||||
let lexer = Parser::new();
|
||||
let token_iter = lexer.parse(src);
|
||||
let mut ps = SourceBuilder {
|
||||
|
@ -422,7 +417,7 @@ mod tests {
|
|||
.ast
|
||||
.into_token_stream()
|
||||
.to_string(),
|
||||
r#"{ __sf_rt :: render_text (__sf_buf , "outer ") ; __sf_rt :: render (__sf_buf , sailfish :: runtime :: filter :: upper (inner)) ? ; __sf_rt :: render_text (__sf_buf , " outer") ; }"#
|
||||
r#"{ __sf_rt :: render_text (__sf_buf , "outer ") ; __sf_rt :: render (__sf_buf , sailfish :: runtime :: filter :: upper ((inner))) ? ; __sf_rt :: render_text (__sf_buf , " outer") ; }"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ extern crate proc_macro;
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro_derive(TemplateOnce, attributes(template))]
|
||||
#[proc_macro_derive(RenderOnce, attributes(template))]
|
||||
pub fn derive_template_once(tokens: TokenStream) -> TokenStream {
|
||||
let input = proc_macro2::TokenStream::from(tokens);
|
||||
let output = sailfish_compiler::procmacro::derive_template(input);
|
||||
|
@ -12,7 +12,7 @@ pub fn derive_template_once(tokens: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
/// WIP
|
||||
#[proc_macro_derive(Template, attributes(template))]
|
||||
#[proc_macro_derive(Render, attributes(template))]
|
||||
pub fn derive_template(tokens: TokenStream) -> TokenStream {
|
||||
let input = proc_macro2::TokenStream::from(tokens);
|
||||
let output = sailfish_compiler::procmacro::derive_template(input);
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[template(path = "foo.stpl", escape=1)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "foo.stpl", escape = 1)]
|
||||
struct InvalidOptionValue {
|
||||
name: String
|
||||
name: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{}", InvalidOptionValue { name: "Hanako".to_owned() }.render_once().unwrap());
|
||||
println!(
|
||||
"{}",
|
||||
InvalidOptionValue {
|
||||
name: "Hanako".to_owned()
|
||||
}
|
||||
.render_once()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ error[E0599]: no method named `render_once` found for struct `InvalidOptionValue
|
|||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `render_once`, perhaps you need to implement it:
|
||||
candidate #1: `TemplateOnce`
|
||||
candidate #1: `RenderOnce`
|
||||
|
||||
warning: unused import: `sailfish::TemplateOnce`
|
||||
warning: unused import: `sailfish::RenderOnce`
|
||||
--> $DIR/invalid_option_value.rs:1:5
|
||||
|
|
||||
1 | use sailfish::TemplateOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
1 | use sailfish::RenderOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "missing_semicolon.stpl")]
|
||||
struct MissingSemicolon {}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ position: line 1, column 17
|
|||
|
||||
--> $DIR/missing_semicolon.rs:4:10
|
||||
|
|
||||
4 | #[derive(TemplateOnce)]
|
||||
| ^^^^^^^^^^^^
|
||||
4 | #[derive(RenderOnce)]
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
|
@ -26,12 +26,12 @@ error[E0599]: no method named `render_once` found for struct `MissingSemicolon`
|
|||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `render_once`, perhaps you need to implement it:
|
||||
candidate #1: `TemplateOnce`
|
||||
candidate #1: `RenderOnce`
|
||||
|
||||
warning: unused import: `sailfish::TemplateOnce`
|
||||
warning: unused import: `sailfish::RenderOnce`
|
||||
--> $DIR/missing_semicolon.rs:1:5
|
||||
|
|
||||
1 | use sailfish::TemplateOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
1 | use sailfish::RenderOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
struct NoTemplate {
|
||||
var: usize
|
||||
var: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: `path` option must be specified.
|
||||
--> $DIR/no_path.rs:4:10
|
||||
|
|
||||
4 | #[derive(TemplateOnce)]
|
||||
| ^^^^^^^^^^^^
|
||||
4 | #[derive(RenderOnce)]
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
|
@ -17,12 +17,12 @@ error[E0599]: no method named `render_once` found for struct `NoTemplate` in the
|
|||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `render_once`, perhaps you need to implement it:
|
||||
candidate #1: `TemplateOnce`
|
||||
candidate #1: `RenderOnce`
|
||||
|
||||
warning: unused import: `sailfish::TemplateOnce`
|
||||
warning: unused import: `sailfish::RenderOnce`
|
||||
--> $DIR/no_path.rs:1:5
|
||||
|
|
||||
1 | use sailfish::TemplateOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
1 | use sailfish::RenderOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[template(path = "foo.stpl", escape=true)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "foo.stpl", escape = true)]
|
||||
#[template(escape = false)]
|
||||
struct InvalidOptionValue {
|
||||
name: String
|
||||
name: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{}", InvalidOptionValue { name: "Hanako".to_owned() }.render_once().unwrap());
|
||||
println!(
|
||||
"{}",
|
||||
InvalidOptionValue {
|
||||
name: "Hanako".to_owned()
|
||||
}
|
||||
.render_once()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ error[E0599]: no method named `render_once` found for struct `InvalidOptionValue
|
|||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `render_once`, perhaps you need to implement it:
|
||||
candidate #1: `TemplateOnce`
|
||||
candidate #1: `RenderOnce`
|
||||
|
||||
warning: unused import: `sailfish::TemplateOnce`
|
||||
warning: unused import: `sailfish::RenderOnce`
|
||||
--> $DIR/repeated_arguments.rs:1:5
|
||||
|
|
||||
1 | use sailfish::TemplateOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
1 | use sailfish::RenderOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "empty.stpl")]
|
||||
struct ExistTemplate;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "not_exist.stpl")]
|
||||
struct NotExistTemplate {
|
||||
var: usize
|
||||
var: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -15,4 +15,4 @@ error[E0599]: no method named `render_once` found for struct `NotExistTemplate`
|
|||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `render_once`, perhaps you need to implement it:
|
||||
candidate #1: `TemplateOnce`
|
||||
candidate #1: `RenderOnce`
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
struct Player<'a> {
|
||||
name: &'a str,
|
||||
score: u32,
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "unbalanced_brace.stpl")]
|
||||
struct UnbalancedBrace {
|
||||
players: Vec<Player>,
|
||||
|
@ -16,7 +16,10 @@ fn main() {
|
|||
println!(
|
||||
"{}",
|
||||
UnclosedDelimiter {
|
||||
players: vec![Player { name: "Hanako", score: 97 }]
|
||||
players: vec![Player {
|
||||
name: "Hanako",
|
||||
score: 97
|
||||
}]
|
||||
}
|
||||
.render_once()
|
||||
.unwrap()
|
||||
|
|
|
@ -5,8 +5,8 @@ file: unbalanced_brace.stpl
|
|||
|
||||
--> $DIR/unbalanced_brace.rs:9:10
|
||||
|
|
||||
9 | #[derive(TemplateOnce)]
|
||||
| ^^^^^^^^^^^^
|
||||
9 | #[derive(RenderOnce)]
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "unclosed_delimiter.stpl")]
|
||||
struct UnclosedDelimiter {
|
||||
content: String,
|
||||
|
|
|
@ -10,8 +10,8 @@ position: line 3, column 5
|
|||
|
||||
--> $DIR/unclosed_delimter.rs:4:10
|
||||
|
|
||||
4 | #[derive(TemplateOnce)]
|
||||
| ^^^^^^^^^^^^
|
||||
4 | #[derive(RenderOnce)]
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
|
@ -26,12 +26,12 @@ error[E0599]: no method named `render_once` found for struct `UnclosedDelimiter`
|
|||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `render_once`, perhaps you need to implement it:
|
||||
candidate #1: `TemplateOnce`
|
||||
candidate #1: `RenderOnce`
|
||||
|
||||
warning: unused import: `sailfish::TemplateOnce`
|
||||
warning: unused import: `sailfish::RenderOnce`
|
||||
--> $DIR/unclosed_delimter.rs:1:5
|
||||
|
|
||||
1 | use sailfish::TemplateOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
1 | use sailfish::RenderOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
struct Content<'a> {
|
||||
id: u32,
|
||||
|
@ -7,12 +7,12 @@ struct Content<'a> {
|
|||
phone_number: &'a str,
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "unexpected_token.stpl")]
|
||||
#[template(escape = false)]
|
||||
struct UnexpectedToken<'a> {
|
||||
name: &'a str,
|
||||
content: Content<'a>
|
||||
content: Content<'a>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -10,8 +10,8 @@ position: line 3, column 17
|
|||
|
||||
--> $DIR/unexpected_token.rs:10:10
|
||||
|
|
||||
10 | #[derive(TemplateOnce)]
|
||||
| ^^^^^^^^^^^^
|
||||
10 | #[derive(RenderOnce)]
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
|
@ -21,10 +21,10 @@ error[E0422]: cannot find struct, variant or union type `UnclosedToken` in this
|
|||
21 | UnclosedToken {
|
||||
| ^^^^^^^^^^^^^ not found in this scope
|
||||
|
||||
warning: unused import: `sailfish::TemplateOnce`
|
||||
warning: unused import: `sailfish::RenderOnce`
|
||||
--> $DIR/unexpected_token.rs:1:5
|
||||
|
|
||||
1 | use sailfish::TemplateOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
1 | use sailfish::RenderOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
use sailfish::TemplateOnce;
|
||||
use sailfish_macros::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use sailfish_macros::RenderOnce;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(patth = "foo.stpl")]
|
||||
struct UnknownOption {
|
||||
name: String
|
||||
name: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{}", UnknownOption { name: "Hanako".to_owned() }.render_once().unwrap());
|
||||
println!(
|
||||
"{}",
|
||||
UnknownOption {
|
||||
name: "Hanako".to_owned()
|
||||
}
|
||||
.render_once()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ error[E0599]: no method named `render_once` found for struct `UnknownOption` in
|
|||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `render_once`, perhaps you need to implement it:
|
||||
candidate #1: `TemplateOnce`
|
||||
candidate #1: `RenderOnce`
|
||||
|
||||
warning: unused import: `sailfish::TemplateOnce`
|
||||
warning: unused import: `sailfish::RenderOnce`
|
||||
--> $DIR/unknown_option.rs:1:5
|
||||
|
|
||||
1 | use sailfish::TemplateOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
1 | use sailfish::RenderOnce;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unused_imports)]` on by default
|
||||
|
|
|
@ -3,7 +3,7 @@ extern crate sailfish_macros;
|
|||
|
||||
use integration_tests::assert_string_eq;
|
||||
use sailfish::runtime::RenderResult;
|
||||
use sailfish::TemplateOnce;
|
||||
use sailfish::RenderOnce;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn assert_render_result(name: &str, result: RenderResult) {
|
||||
|
@ -23,8 +23,8 @@ fn assert_render_result(name: &str, result: RenderResult) {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_render<T: TemplateOnce>(name: &str, template: T) {
|
||||
assert_render_result(name, template.render_once());
|
||||
fn assert_render<T: RenderOnce>(name: &str, template: T) {
|
||||
assert_render_result(name, template.render_once_to_string());
|
||||
}
|
||||
|
||||
trait ConflictWithSailFishRender {
|
||||
|
@ -34,7 +34,7 @@ trait ConflictWithSailFishRender {
|
|||
impl ConflictWithSailFishRender for u8 {}
|
||||
impl ConflictWithSailFishRender for u16 {}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "empty.stpl")]
|
||||
struct Empty {}
|
||||
|
||||
|
@ -43,7 +43,7 @@ fn empty() {
|
|||
assert_render("empty", Empty {});
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "noescape.stpl")]
|
||||
struct Noescape<'a> {
|
||||
raw: &'a str,
|
||||
|
@ -59,7 +59,7 @@ fn noescape() {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "json.stpl")]
|
||||
struct Json {
|
||||
name: String,
|
||||
|
@ -77,7 +77,7 @@ fn json() {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "custom_delimiter.stpl")]
|
||||
#[template(delimiter = '🍣')]
|
||||
struct CustomDelimiter;
|
||||
|
@ -87,7 +87,7 @@ fn custom_delimiter() {
|
|||
assert_render("custom_delimiter", CustomDelimiter);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "include.stpl")]
|
||||
struct Include<'a> {
|
||||
strs: &'a [&'a str],
|
||||
|
@ -103,7 +103,7 @@ fn test_include() {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "continue-break.stpl", rm_whitespace = true)]
|
||||
struct ContinueBreak;
|
||||
|
||||
|
@ -112,7 +112,7 @@ fn continue_break() {
|
|||
assert_render("continue-break", ContinueBreak);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "techempower.stpl", rm_whitespace = true)]
|
||||
struct Techempower {
|
||||
items: Vec<Fortune>,
|
||||
|
@ -182,7 +182,7 @@ fn test_techempower() {
|
|||
assert_render("techempower", Techempower { items });
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "rm_whitespace.stpl")]
|
||||
#[template(rm_whitespace = true)]
|
||||
struct RmWhitespace<'a, 'b> {
|
||||
|
@ -199,7 +199,7 @@ fn test_rm_whitespace() {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "comment.stpl")]
|
||||
struct Comment {}
|
||||
|
||||
|
@ -208,7 +208,7 @@ fn test_comment() {
|
|||
assert_render("comment", Comment {})
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "rust_macro.stpl", rm_whitespace = true)]
|
||||
struct RustMacro {
|
||||
value: Option<i32>,
|
||||
|
@ -219,7 +219,7 @@ fn test_rust_macro() {
|
|||
assert_render("rust_macro", RustMacro { value: Some(10) });
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "formatting.stpl", escape = false)]
|
||||
struct Formatting;
|
||||
|
||||
|
@ -228,7 +228,7 @@ fn test_formatting() {
|
|||
assert_render("formatting", Formatting);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "filter.stpl")]
|
||||
struct Filter<'a> {
|
||||
message: &'a str,
|
||||
|
@ -239,7 +239,7 @@ fn test_filter() {
|
|||
assert_render("filter", Filter { message: "hello" });
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "filter2.stpl")]
|
||||
struct Filter2;
|
||||
|
||||
|
@ -248,7 +248,7 @@ fn test_filter2() {
|
|||
assert_render("filter2", Filter2);
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "truncate-filter.stpl")]
|
||||
struct TruncateFilter;
|
||||
|
||||
|
@ -256,7 +256,7 @@ struct TruncateFilter;
|
|||
fn test_truncate_filter() {
|
||||
assert_render("truncate-filter", TruncateFilter);
|
||||
}
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "json-filter.stpl")]
|
||||
struct JsonFilter {
|
||||
data: serde_json::Value,
|
||||
|
@ -280,7 +280,7 @@ fn test_json_filter() {
|
|||
mod unix {
|
||||
use super::*;
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "include-nest.stpl")]
|
||||
struct IncludeNest<'a> {
|
||||
s: &'a str,
|
||||
|
@ -291,7 +291,7 @@ mod unix {
|
|||
assert_render("include-nest", IncludeNest { s: "foo" });
|
||||
}
|
||||
|
||||
#[derive(TemplateOnce)]
|
||||
#[derive(RenderOnce)]
|
||||
#[template(path = "include_rust.stpl")]
|
||||
struct IncludeRust {
|
||||
value: usize,
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
//!
|
||||
//! This crate contains utilities for rendering sailfish template.
|
||||
//! If you want to use sailfish templates, import `sailfish-macros` crate and use
|
||||
//! derive macro `#[derive(TemplateOnce)]` or `#[derive(Template)]`.
|
||||
//! derive macro `#[derive(RenderOnce)]` or `#[derive(Render)]`.
|
||||
//!
|
||||
//! In most cases you don't need to care about the `runtime` module in this crate, but
|
||||
//! if you want to render custom data inside templates, you must implement
|
||||
//! `runtime::Render` trait for that type.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! use sailfish::TemplateOnce;
|
||||
//! use sailfish::RenderOnce;
|
||||
//!
|
||||
//! #[derive(TemplateOnce)]
|
||||
//! #[derive(RenderOnce)]
|
||||
//! #[template(path = "hello.stpl")]
|
||||
//! struct HelloTemplate {
|
||||
//! messages: Vec<String>
|
||||
|
@ -35,92 +35,10 @@
|
|||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![allow(clippy::redundant_closure)]
|
||||
#![deny(missing_docs)]
|
||||
#![feature(specialization)]
|
||||
|
||||
pub mod runtime;
|
||||
|
||||
use runtime::Buffer;
|
||||
pub use runtime::{RenderError, RenderResult};
|
||||
pub use runtime::{Buffer, Render, RenderError, RenderOnce, RenderResult};
|
||||
#[cfg(feature = "derive")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
|
||||
pub use sailfish_macros::TemplateOnce;
|
||||
|
||||
/// Template that can be rendered with consuming itself.
|
||||
pub trait TemplateOnce: Sized {
|
||||
/// Render the template and return the rendering result as `RenderResult`
|
||||
///
|
||||
/// This method never returns `Err`, unless you explicitly return RenderError
|
||||
/// inside templates
|
||||
///
|
||||
/// When you use `render_once` method, total rendered size will be cached, and at
|
||||
/// the next time, buffer will be pre-allocated based on the cached length.
|
||||
///
|
||||
/// If you don't want this behaviour, you can use `render_once_to` method instead.
|
||||
fn render_once(self) -> runtime::RenderResult;
|
||||
|
||||
/// Render the template and append the result to `buf`.
|
||||
///
|
||||
/// This method never returns `Err`, unless you explicitly return RenderError
|
||||
/// inside templates
|
||||
///
|
||||
/// ```
|
||||
/// use sailfish::TemplateOnce;
|
||||
/// use sailfish::runtime::Buffer;
|
||||
///
|
||||
/// # pub struct HelloTemplate {
|
||||
/// # messages: Vec<String>,
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl TemplateOnce for HelloTemplate {
|
||||
/// # fn render_once(self) -> Result<String, sailfish::RenderError> {
|
||||
/// # Ok(String::new())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn render_once_to(self, buf: &mut Buffer)
|
||||
/// # -> Result<(), sailfish::RenderError> {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl sailfish::private::Sealed for HelloTemplate {}
|
||||
/// #
|
||||
/// let tpl = HelloTemplate {
|
||||
/// messages: vec!["foo".to_string()]
|
||||
/// };
|
||||
///
|
||||
/// // custom pre-allocation
|
||||
/// let mut buffer = Buffer::with_capacity(100);
|
||||
/// tpl.render_once_to(&mut buffer).unwrap();
|
||||
/// ```
|
||||
fn render_once_to(self, buf: &mut Buffer) -> Result<(), RenderError>;
|
||||
}
|
||||
|
||||
/// Work in Progress
|
||||
pub trait Template
|
||||
where
|
||||
for<'a> &'a Self: TemplateOnce,
|
||||
{
|
||||
/// Work in progress
|
||||
fn render(&self) -> runtime::RenderResult;
|
||||
|
||||
/// Work in progress
|
||||
fn render_to(&self, buf: &mut Buffer) -> Result<(), RenderError>;
|
||||
}
|
||||
|
||||
impl<T> Template for T
|
||||
where
|
||||
for<'a> &'a T: TemplateOnce,
|
||||
{
|
||||
fn render(&self) -> runtime::RenderResult {
|
||||
TemplateOnce::render_once(self)
|
||||
}
|
||||
|
||||
fn render_to(&self, buf: &mut Buffer) -> Result<(), RenderError> {
|
||||
TemplateOnce::render_once_to(self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
pub use sailfish_macros::{Render, RenderOnce};
|
||||
|
|
|
@ -9,6 +9,8 @@ use std::path::{Path, PathBuf};
|
|||
use std::rc::Rc;
|
||||
use std::sync::{Arc, MutexGuard, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use crate::runtime::SizeHint;
|
||||
|
||||
use super::buffer::Buffer;
|
||||
use super::escape;
|
||||
|
||||
|
@ -57,6 +59,33 @@ pub trait Render {
|
|||
/// See [`Render`] for more information.
|
||||
pub trait RenderOnce: Sized {
|
||||
/// render to `Buffer` without escaping
|
||||
///
|
||||
/// Render the template and append the result to `buf`.
|
||||
///
|
||||
/// This method never returns `Err`, unless you explicitly return RenderError
|
||||
/// inside templates
|
||||
///
|
||||
/// ```should_fail
|
||||
/// use sailfish::{Buffer, RenderOnce};
|
||||
///
|
||||
/// # pub struct HelloTemplate {
|
||||
/// # messages: Vec<String>,
|
||||
/// # }
|
||||
/// #
|
||||
/// # impl RenderOnce for HelloTemplate {
|
||||
/// # fn render_once(self, buf: &mut Buffer) -> Result<(), sailfish::RenderError> {
|
||||
/// # unimplemented!()
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// let tpl = HelloTemplate {
|
||||
/// messages: vec!["foo".to_string()]
|
||||
/// };
|
||||
///
|
||||
/// // custom pre-allocation
|
||||
/// let mut buffer = Buffer::with_capacity(100);
|
||||
/// tpl.render_once(&mut buffer).unwrap();
|
||||
/// ```
|
||||
fn render_once(self, b: &mut Buffer) -> Result<(), RenderError>;
|
||||
|
||||
/// render to `Buffer` with HTML escaping
|
||||
|
@ -67,6 +96,26 @@ pub trait RenderOnce: Sized {
|
|||
escape::escape_to_buf(tmp.as_str(), b);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Render the template and return the rendering result as `RenderResult`
|
||||
///
|
||||
/// This method never returns `Err`, unless you explicitly return RenderError
|
||||
/// inside templates
|
||||
///
|
||||
/// When you use `render_once_to_string` method, total rendered size will be cached,
|
||||
/// and at the next time, buffer will be pre-allocated based on the cached length.
|
||||
///
|
||||
/// If you don't want this behaviour, you can use `render_once` method instead.
|
||||
#[inline]
|
||||
fn render_once_to_string(self) -> RenderResult {
|
||||
static SIZE_HINT: SizeHint = SizeHint::new();
|
||||
|
||||
let mut buf = Buffer::with_capacity(SIZE_HINT.get());
|
||||
self.render_once(&mut buf)?;
|
||||
SIZE_HINT.update(buf.len());
|
||||
|
||||
Ok(buf.into_string())
|
||||
}
|
||||
}
|
||||
|
||||
// impl<'a, T: ?Sized> Render for &'a T
|
||||
|
@ -86,10 +135,12 @@ impl<T> RenderOnce for T
|
|||
where
|
||||
T: Render,
|
||||
{
|
||||
#[inline]
|
||||
fn render_once(self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||
self.render(b)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn render_once_escaped(self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||
self.render_escaped(b)
|
||||
}
|
||||
|
@ -353,7 +404,7 @@ macro_rules! render_deref {
|
|||
|
||||
render_deref!(['a, T: Render + ?Sized] [] &'a T);
|
||||
render_deref!(['a, T: Render + ?Sized] [] &'a mut T);
|
||||
render_deref!(default[default] [T: Render + ?Sized] [] Box<T>);
|
||||
render_deref!([T: Render + ?Sized] [] Box<T>);
|
||||
render_deref!([T: Render + ?Sized] [] Rc<T>);
|
||||
render_deref!([T: Render + ?Sized] [] Arc<T>);
|
||||
render_deref!(['a, T: Render + ToOwned + ?Sized] [] Cow<'a, T>);
|
||||
|
@ -457,7 +508,7 @@ impl From<fmt::Error> for RenderError {
|
|||
}
|
||||
}
|
||||
|
||||
/// Result type returned from `TemplateOnce::render_once` method
|
||||
/// Result type returned from [`RenderOnce::render_once_to_string`] method
|
||||
pub type RenderResult = Result<String, RenderError>;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
Loading…
Reference in New Issue