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
|
## ✨ Features
|
||||||
|
|
||||||
- Simple and intuitive syntax inspired by [EJS](https://ejs.co/)
|
- Simple and intuitive syntax inspired by [EJS](https://ejs.co/)
|
||||||
- Include another template file inside template
|
- Include another template file inside template
|
||||||
- Built-in filters
|
- Built-in filters
|
||||||
- Minimal dependencies (<15 crates in total)
|
- Minimal dependencies (<15 crates in total)
|
||||||
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
||||||
- Better error message
|
- Better error message
|
||||||
- Syntax highlighting support ([vscode](./syntax/vscode), [vim](./syntax/vim))
|
- Syntax highlighting support ([vscode](./syntax/vscode), [vim](./syntax/vim))
|
||||||
- Works on Rust 1.60 or later
|
- Works on Rust 1.60 or later
|
||||||
|
|
||||||
## 🐟 Example
|
## 🐟 Example
|
||||||
|
|
||||||
|
@ -49,9 +49,9 @@ Template file (templates/hello.stpl):
|
||||||
Code:
|
Code:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "hello.stpl")]
|
#[template(path = "hello.stpl")]
|
||||||
struct HelloTemplate {
|
struct HelloTemplate {
|
||||||
messages: Vec<String>
|
messages: Vec<String>
|
||||||
|
@ -69,14 +69,14 @@ You can find more examples in [examples](./examples) directory.
|
||||||
|
|
||||||
## 🐾 Roadmap
|
## 🐾 Roadmap
|
||||||
|
|
||||||
- `Template` trait ([RFC](https://github.com/rust-sailfish/sailfish/issues/3))
|
- `Render` derive macro ([RFC](https://github.com/rust-sailfish/sailfish/issues/3))
|
||||||
- Template inheritance (block, partials, etc.)
|
- Template inheritance (block, partials, etc.)
|
||||||
|
|
||||||
## 👤 Author
|
## 👤 Author
|
||||||
|
|
||||||
🇯🇵 **Ryohei Machida**
|
🇯🇵 **Ryohei Machida**
|
||||||
|
|
||||||
* GitHub: [@Kogia-sima](https://github.com/Kogia-sima)
|
- GitHub: [@Kogia-sima](https://github.com/Kogia-sima)
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 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 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)_
|
_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`.
|
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>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<% for msg in &messages { %>
|
<% for msg in &messages { %>
|
||||||
|
@ -29,13 +29,13 @@ templates/
|
||||||
<ol><li>Import the sailfish crates:</li></ol>
|
<ol><li>Import the sailfish crates:</li></ol>
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
```
|
```
|
||||||
|
|
||||||
<ol start="2"><li>Define the template struct to be rendered:</li></ol>
|
<ol start="2"><li>Define the template struct to be rendered:</li></ol>
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[derive(TemplateOnce)] // automatically implement `TemplateOnce` trait
|
#[derive(RenderOnce)] // automatically implement `TemplateOnce` trait
|
||||||
#[template(path = "hello.stpl")] // specify the path to template
|
#[template(path = "hello.stpl")] // specify the path to template
|
||||||
struct HelloTemplate {
|
struct HelloTemplate {
|
||||||
// data to be passed to the template
|
// 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.
|
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.)
|
- 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)
|
- [Built-in filters](https://docs.rs/sailfish/latest/sailfish/runtime/filter/index.html)
|
||||||
- Minimal dependencies (<15 crates in total)
|
- Minimal dependencies (<15 crates in total)
|
||||||
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
- Extremely fast (See [benchmarks](https://github.com/djc/template-benchmarks-rs))
|
||||||
- Template rendering is always type-safe because templates are statically compiled.
|
- 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))
|
- 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
|
## 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:
|
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)
|
- `Render` derive macro (which does not consume itself)
|
||||||
- Template inheritance (block, partials, etc.)
|
- 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!
|
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.
|
You can control the rendering behaviour via `template` attribute.
|
||||||
|
|
||||||
``` rust
|
```rust
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "template.stpl", escape = false)]
|
#[template(path = "template.stpl", escape = false)]
|
||||||
struct TemplateStruct {
|
struct TemplateStruct {
|
||||||
...
|
...
|
||||||
|
@ -14,15 +14,15 @@ struct TemplateStruct {
|
||||||
|
|
||||||
`template` attribute accepts the following options.
|
`template` attribute accepts the following options.
|
||||||
|
|
||||||
- `path`: path to template file. This options is always required.
|
- `path`: path to template file. This options is always required.
|
||||||
- `escape`: Enable HTML escaping (default: `true`)
|
- `escape`: Enable HTML escaping (default: `true`)
|
||||||
- `delimiter`: Replace the '%' character used for the tag delimiter (default: '%')
|
- `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.
|
- `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.
|
You can split the options into multiple `template` attributes.
|
||||||
|
|
||||||
``` rust
|
```rust
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "template.stpl")]
|
#[template(path = "template.stpl")]
|
||||||
#[template(delimiter = '?')]
|
#[template(delimiter = '?')]
|
||||||
#[template(rm_whitespace = true)]
|
#[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.
|
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.
|
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/baz/sailfish.toml`
|
||||||
- `/foo/bar/sailfish.toml`
|
- `/foo/bar/sailfish.toml`
|
||||||
- `/foo/sailfish.toml`
|
- `/foo/sailfish.toml`
|
||||||
- `/sailfish.toml`
|
- `/sailfish.toml`
|
||||||
|
|
||||||
If a key is specified in multiple configuration files, the value in the deeper directory takes precedence over ancestor directories.
|
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:
|
Configuration files are written in the TOML 0.5 format. Here is the default configuration:
|
||||||
|
|
||||||
``` toml
|
```toml
|
||||||
template_dirs = ["templates"]
|
template_dirs = ["templates"]
|
||||||
escape = true
|
escape = true
|
||||||
delimiter = "%"
|
delimiter = "%"
|
||||||
|
|
|
@ -2,16 +2,15 @@
|
||||||
|
|
||||||
## Tags
|
## Tags
|
||||||
|
|
||||||
- `<% %>`: Inline tag, you can write Rust code inside this tag
|
- `<% %>`: 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 value into the template (HTML escaped)
|
||||||
- `<%- %>`: Evaluate the Rust expression and outputs the unescaped value into the template
|
- `<%- %>`: 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
|
||||||
- `<%# %>`: Comment tag
|
- `<%%`: Outputs a literal '<%'
|
||||||
- `<%%`: Outputs a literal '<%'
|
|
||||||
|
|
||||||
## Condition
|
## Condition
|
||||||
|
|
||||||
``` rhtml
|
```rhtml
|
||||||
<% if messages.is_empty() { %>
|
<% if messages.is_empty() { %>
|
||||||
<div>No messages</div>
|
<div>No messages</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
@ -19,7 +18,7 @@
|
||||||
|
|
||||||
## loop
|
## loop
|
||||||
|
|
||||||
``` rhtml
|
```rhtml
|
||||||
<% for (i, msg) in messages.iter().enumerate() { %>
|
<% for (i, msg) in messages.iter().enumerate() { %>
|
||||||
<div><%= i %>: <%= msg %></div>
|
<div><%= i %>: <%= msg %></div>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
@ -27,17 +26,17 @@
|
||||||
|
|
||||||
## Includes
|
## Includes
|
||||||
|
|
||||||
``` rhtml
|
```rhtml
|
||||||
<% include!("path/to/template"); %>
|
<% include!("path/to/template"); %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Filters
|
## Filters
|
||||||
|
|
||||||
``` rhtml
|
```rhtml
|
||||||
<%= message | upper %>
|
<%= message | upper %>
|
||||||
```
|
```
|
||||||
|
|
||||||
``` rhtml
|
```rhtml
|
||||||
{
|
{
|
||||||
"id": <%= id %>
|
"id": <%= id %>
|
||||||
"comment": <%- comment | json %>
|
"comment": <%- comment | json %>
|
||||||
|
|
|
@ -26,7 +26,7 @@ You can write Rust statement inside `<% %>` tag.
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! Note
|
!!! 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.
|
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.
|
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
|
- Function/Macro definition that render some contents
|
||||||
- `impl` item
|
- `impl` item
|
||||||
- Macro call which defines some local variable.
|
- Macro call which defines some local variable.
|
||||||
- Macro call which behaviour depends on the path to source file
|
- Macro call which behaviour depends on the path to source file
|
||||||
- Generator expression (yield)
|
- Generator expression (yield)
|
||||||
|
|
||||||
## Evaluation block
|
## Evaluation block
|
||||||
|
|
||||||
|
@ -100,35 +100,8 @@ If you want to render the results without escaping, you can use `<%- %>` tag or
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! Note
|
!!! 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
|
``` rhtml
|
||||||
<% let result = %><%= 1 %><% ; %>
|
<% 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::error::InternalError;
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "actix.stpl")]
|
#[template(path = "actix.stpl")]
|
||||||
struct Greet<'a> {
|
struct Greet<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "include.stpl")]
|
#[template(path = "include.stpl")]
|
||||||
struct Include {
|
struct Include {
|
||||||
title: String,
|
title: String,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "simple.stpl")]
|
#[template(path = "simple.stpl")]
|
||||||
struct Simple {
|
struct Simple {
|
||||||
messages: Vec<String>,
|
messages: Vec<String>,
|
||||||
|
|
|
@ -56,7 +56,6 @@ impl Default for Parser {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum TokenKind {
|
pub enum TokenKind {
|
||||||
NestedTemplateOnce,
|
|
||||||
BufferedCode { escape: bool },
|
BufferedCode { escape: bool },
|
||||||
Code,
|
Code,
|
||||||
Comment,
|
Comment,
|
||||||
|
@ -162,10 +161,6 @@ impl<'a> ParseStream<'a> {
|
||||||
token_kind = TokenKind::BufferedCode { escape: false };
|
token_kind = TokenKind::BufferedCode { escape: false };
|
||||||
start += 1;
|
start += 1;
|
||||||
}
|
}
|
||||||
Some(b'+') => {
|
|
||||||
token_kind = TokenKind::NestedTemplateOnce;
|
|
||||||
start += 1;
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,7 +404,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_render_once() {
|
fn nested_render_once() {
|
||||||
let src = r#"outer <%+ inner|upper %> outer"#;
|
let src = r#"outer <%- inner | upper %> outer"#;
|
||||||
let parser = Parser::default();
|
let parser = Parser::default();
|
||||||
let tokens = parser.parse(src).into_vec().unwrap();
|
let tokens = parser.parse(src).into_vec().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -421,13 +416,13 @@ mod tests {
|
||||||
kind: TokenKind::Text,
|
kind: TokenKind::Text,
|
||||||
},
|
},
|
||||||
Token {
|
Token {
|
||||||
content: "inner|upper",
|
content: "inner | upper",
|
||||||
offset: 10,
|
offset: 10,
|
||||||
kind: TokenKind::NestedTemplateOnce,
|
kind: TokenKind::BufferedCode { escape: false },
|
||||||
},
|
},
|
||||||
Token {
|
Token {
|
||||||
content: " outer",
|
content: " outer",
|
||||||
offset: 24,
|
offset: 26,
|
||||||
kind: TokenKind::Text,
|
kind: TokenKind::Text,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -342,30 +342,23 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
Span::call_site(),
|
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();
|
let (impl_generics, ty_generics, where_clause) = strct.generics.split_for_impl();
|
||||||
|
|
||||||
// render_once method always results in the same code.
|
let inline = if cfg!(feature = "perf-inline") {
|
||||||
// This method can be implemented in `sailfish` crate, but I found that performance
|
Some(quote!(#[inline]))
|
||||||
// drops when the implementation is written in `sailfish` crate.
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let tokens = quote! {
|
let tokens = quote! {
|
||||||
impl #impl_generics sailfish::TemplateOnce for #name #ty_generics #where_clause {
|
impl #impl_generics sailfish::RenderOnce for #name #ty_generics #where_clause {
|
||||||
fn render_once(self) -> sailfish::RenderResult {
|
#inline
|
||||||
use sailfish::runtime::{Buffer, SizeHint};
|
fn render_once(self, __sf_buf: &mut sailfish::runtime::Buffer) -> std::result::Result<(), sailfish::runtime::RenderError> {
|
||||||
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> {
|
|
||||||
// This line is required for cargo to track child templates
|
// This line is required for cargo to track child templates
|
||||||
#include_bytes_seq;
|
#include_bytes_seq;
|
||||||
use sailfish::runtime as __sf_rt;
|
use sailfish::runtime as __sf_rt;
|
||||||
|
@ -375,8 +368,6 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #impl_generics sailfish::private::Sealed for #name #ty_generics #where_clause {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
|
|
|
@ -214,11 +214,6 @@ impl SourceBuilder {
|
||||||
TokenKind::BufferedCode { escape } => {
|
TokenKind::BufferedCode { escape } => {
|
||||||
self.write_buffered_code(&token, escape)?
|
self.write_buffered_code(&token, escape)?
|
||||||
}
|
}
|
||||||
TokenKind::NestedTemplateOnce => self.write_buffered_code_with_suffix(
|
|
||||||
&token,
|
|
||||||
false,
|
|
||||||
".render_once()?",
|
|
||||||
)?,
|
|
||||||
TokenKind::Text => {
|
TokenKind::Text => {
|
||||||
// concatenate repeated text token
|
// concatenate repeated text token
|
||||||
let offset = token.offset();
|
let offset = token.offset();
|
||||||
|
@ -384,7 +379,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn translate_nested_render_once() {
|
fn translate_nested_render_once() {
|
||||||
let src = r#"outer <%+ inner %> outer"#;
|
let src = r#"outer <%- inner %> outer"#;
|
||||||
let lexer = Parser::new();
|
let lexer = Parser::new();
|
||||||
let token_iter = lexer.parse(src);
|
let token_iter = lexer.parse(src);
|
||||||
let mut ps = SourceBuilder {
|
let mut ps = SourceBuilder {
|
||||||
|
@ -400,13 +395,13 @@ mod tests {
|
||||||
.ast
|
.ast
|
||||||
.into_token_stream()
|
.into_token_stream()
|
||||||
.to_string(),
|
.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]
|
#[test]
|
||||||
fn translate_nested_render_once_with_filter() {
|
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 lexer = Parser::new();
|
||||||
let token_iter = lexer.parse(src);
|
let token_iter = lexer.parse(src);
|
||||||
let mut ps = SourceBuilder {
|
let mut ps = SourceBuilder {
|
||||||
|
@ -422,7 +417,7 @@ mod tests {
|
||||||
.ast
|
.ast
|
||||||
.into_token_stream()
|
.into_token_stream()
|
||||||
.to_string(),
|
.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;
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
#[proc_macro_derive(TemplateOnce, attributes(template))]
|
#[proc_macro_derive(RenderOnce, attributes(template))]
|
||||||
pub fn derive_template_once(tokens: TokenStream) -> TokenStream {
|
pub fn derive_template_once(tokens: TokenStream) -> TokenStream {
|
||||||
let input = proc_macro2::TokenStream::from(tokens);
|
let input = proc_macro2::TokenStream::from(tokens);
|
||||||
let output = sailfish_compiler::procmacro::derive_template(input);
|
let output = sailfish_compiler::procmacro::derive_template(input);
|
||||||
|
@ -12,7 +12,7 @@ pub fn derive_template_once(tokens: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WIP
|
/// WIP
|
||||||
#[proc_macro_derive(Template, attributes(template))]
|
#[proc_macro_derive(Render, attributes(template))]
|
||||||
pub fn derive_template(tokens: TokenStream) -> TokenStream {
|
pub fn derive_template(tokens: TokenStream) -> TokenStream {
|
||||||
let input = proc_macro2::TokenStream::from(tokens);
|
let input = proc_macro2::TokenStream::from(tokens);
|
||||||
let output = sailfish_compiler::procmacro::derive_template(input);
|
let output = sailfish_compiler::procmacro::derive_template(input);
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "foo.stpl", escape=1)]
|
#[template(path = "foo.stpl", escape = 1)]
|
||||||
struct InvalidOptionValue {
|
struct InvalidOptionValue {
|
||||||
name: String
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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
|
= 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:
|
= 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
|
--> $DIR/invalid_option_value.rs:1:5
|
||||||
|
|
|
|
||||||
1 | use sailfish::TemplateOnce;
|
1 | use sailfish::RenderOnce;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unused_imports)]` on by default
|
= note: `#[warn(unused_imports)]` on by default
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "missing_semicolon.stpl")]
|
#[template(path = "missing_semicolon.stpl")]
|
||||||
struct MissingSemicolon {}
|
struct MissingSemicolon {}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ position: line 1, column 17
|
||||||
|
|
||||||
--> $DIR/missing_semicolon.rs:4:10
|
--> $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)
|
= 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
|
= 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:
|
= 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
|
--> $DIR/missing_semicolon.rs:1:5
|
||||||
|
|
|
|
||||||
1 | use sailfish::TemplateOnce;
|
1 | use sailfish::RenderOnce;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unused_imports)]` on by default
|
= note: `#[warn(unused_imports)]` on by default
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
struct NoTemplate {
|
struct NoTemplate {
|
||||||
var: usize
|
var: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
error: `path` option must be specified.
|
error: `path` option must be specified.
|
||||||
--> $DIR/no_path.rs:4:10
|
--> $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)
|
= 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
|
= 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:
|
= 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
|
--> $DIR/no_path.rs:1:5
|
||||||
|
|
|
|
||||||
1 | use sailfish::TemplateOnce;
|
1 | use sailfish::RenderOnce;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unused_imports)]` on by default
|
= note: `#[warn(unused_imports)]` on by default
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "foo.stpl", escape=true)]
|
#[template(path = "foo.stpl", escape = true)]
|
||||||
#[template(escape = false)]
|
#[template(escape = false)]
|
||||||
struct InvalidOptionValue {
|
struct InvalidOptionValue {
|
||||||
name: String
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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
|
= 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:
|
= 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
|
--> $DIR/repeated_arguments.rs:1:5
|
||||||
|
|
|
|
||||||
1 | use sailfish::TemplateOnce;
|
1 | use sailfish::RenderOnce;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unused_imports)]` on by default
|
= note: `#[warn(unused_imports)]` on by default
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "empty.stpl")]
|
#[template(path = "empty.stpl")]
|
||||||
struct ExistTemplate;
|
struct ExistTemplate;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "not_exist.stpl")]
|
#[template(path = "not_exist.stpl")]
|
||||||
struct NotExistTemplate {
|
struct NotExistTemplate {
|
||||||
var: usize
|
var: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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
|
= 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:
|
= 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::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
struct Player<'a> {
|
struct Player<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
score: u32,
|
score: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "unbalanced_brace.stpl")]
|
#[template(path = "unbalanced_brace.stpl")]
|
||||||
struct UnbalancedBrace {
|
struct UnbalancedBrace {
|
||||||
players: Vec<Player>,
|
players: Vec<Player>,
|
||||||
|
@ -16,7 +16,10 @@ fn main() {
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
UnclosedDelimiter {
|
UnclosedDelimiter {
|
||||||
players: vec![Player { name: "Hanako", score: 97 }]
|
players: vec![Player {
|
||||||
|
name: "Hanako",
|
||||||
|
score: 97
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
.render_once()
|
.render_once()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
|
@ -5,8 +5,8 @@ file: unbalanced_brace.stpl
|
||||||
|
|
||||||
--> $DIR/unbalanced_brace.rs:9:10
|
--> $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)
|
= 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::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "unclosed_delimiter.stpl")]
|
#[template(path = "unclosed_delimiter.stpl")]
|
||||||
struct UnclosedDelimiter {
|
struct UnclosedDelimiter {
|
||||||
content: String,
|
content: String,
|
||||||
|
|
|
@ -10,8 +10,8 @@ position: line 3, column 5
|
||||||
|
|
||||||
--> $DIR/unclosed_delimter.rs:4:10
|
--> $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)
|
= 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
|
= 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:
|
= 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
|
--> $DIR/unclosed_delimter.rs:1:5
|
||||||
|
|
|
|
||||||
1 | use sailfish::TemplateOnce;
|
1 | use sailfish::RenderOnce;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unused_imports)]` on by default
|
= note: `#[warn(unused_imports)]` on by default
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
struct Content<'a> {
|
struct Content<'a> {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
@ -7,12 +7,12 @@ struct Content<'a> {
|
||||||
phone_number: &'a str,
|
phone_number: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "unexpected_token.stpl")]
|
#[template(path = "unexpected_token.stpl")]
|
||||||
#[template(escape = false)]
|
#[template(escape = false)]
|
||||||
struct UnexpectedToken<'a> {
|
struct UnexpectedToken<'a> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
content: Content<'a>
|
content: Content<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -10,8 +10,8 @@ position: line 3, column 17
|
||||||
|
|
||||||
--> $DIR/unexpected_token.rs:10:10
|
--> $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)
|
= 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 {
|
21 | UnclosedToken {
|
||||||
| ^^^^^^^^^^^^^ not found in this scope
|
| ^^^^^^^^^^^^^ not found in this scope
|
||||||
|
|
||||||
warning: unused import: `sailfish::TemplateOnce`
|
warning: unused import: `sailfish::RenderOnce`
|
||||||
--> $DIR/unexpected_token.rs:1:5
|
--> $DIR/unexpected_token.rs:1:5
|
||||||
|
|
|
|
||||||
1 | use sailfish::TemplateOnce;
|
1 | use sailfish::RenderOnce;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unused_imports)]` on by default
|
= note: `#[warn(unused_imports)]` on by default
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use sailfish_macros::TemplateOnce;
|
use sailfish_macros::RenderOnce;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(patth = "foo.stpl")]
|
#[template(patth = "foo.stpl")]
|
||||||
struct UnknownOption {
|
struct UnknownOption {
|
||||||
name: String
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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
|
= 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:
|
= 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
|
--> $DIR/unknown_option.rs:1:5
|
||||||
|
|
|
|
||||||
1 | use sailfish::TemplateOnce;
|
1 | use sailfish::RenderOnce;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unused_imports)]` on by default
|
= note: `#[warn(unused_imports)]` on by default
|
||||||
|
|
|
@ -3,7 +3,7 @@ extern crate sailfish_macros;
|
||||||
|
|
||||||
use integration_tests::assert_string_eq;
|
use integration_tests::assert_string_eq;
|
||||||
use sailfish::runtime::RenderResult;
|
use sailfish::runtime::RenderResult;
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::RenderOnce;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn assert_render_result(name: &str, result: RenderResult) {
|
fn assert_render_result(name: &str, result: RenderResult) {
|
||||||
|
@ -23,8 +23,8 @@ fn assert_render_result(name: &str, result: RenderResult) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn assert_render<T: TemplateOnce>(name: &str, template: T) {
|
fn assert_render<T: RenderOnce>(name: &str, template: T) {
|
||||||
assert_render_result(name, template.render_once());
|
assert_render_result(name, template.render_once_to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ConflictWithSailFishRender {
|
trait ConflictWithSailFishRender {
|
||||||
|
@ -34,7 +34,7 @@ trait ConflictWithSailFishRender {
|
||||||
impl ConflictWithSailFishRender for u8 {}
|
impl ConflictWithSailFishRender for u8 {}
|
||||||
impl ConflictWithSailFishRender for u16 {}
|
impl ConflictWithSailFishRender for u16 {}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "empty.stpl")]
|
#[template(path = "empty.stpl")]
|
||||||
struct Empty {}
|
struct Empty {}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ fn empty() {
|
||||||
assert_render("empty", Empty {});
|
assert_render("empty", Empty {});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "noescape.stpl")]
|
#[template(path = "noescape.stpl")]
|
||||||
struct Noescape<'a> {
|
struct Noescape<'a> {
|
||||||
raw: &'a str,
|
raw: &'a str,
|
||||||
|
@ -59,7 +59,7 @@ fn noescape() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "json.stpl")]
|
#[template(path = "json.stpl")]
|
||||||
struct Json {
|
struct Json {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -77,7 +77,7 @@ fn json() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "custom_delimiter.stpl")]
|
#[template(path = "custom_delimiter.stpl")]
|
||||||
#[template(delimiter = '🍣')]
|
#[template(delimiter = '🍣')]
|
||||||
struct CustomDelimiter;
|
struct CustomDelimiter;
|
||||||
|
@ -87,7 +87,7 @@ fn custom_delimiter() {
|
||||||
assert_render("custom_delimiter", CustomDelimiter);
|
assert_render("custom_delimiter", CustomDelimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "include.stpl")]
|
#[template(path = "include.stpl")]
|
||||||
struct Include<'a> {
|
struct Include<'a> {
|
||||||
strs: &'a [&'a str],
|
strs: &'a [&'a str],
|
||||||
|
@ -103,7 +103,7 @@ fn test_include() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "continue-break.stpl", rm_whitespace = true)]
|
#[template(path = "continue-break.stpl", rm_whitespace = true)]
|
||||||
struct ContinueBreak;
|
struct ContinueBreak;
|
||||||
|
|
||||||
|
@ -112,7 +112,7 @@ fn continue_break() {
|
||||||
assert_render("continue-break", ContinueBreak);
|
assert_render("continue-break", ContinueBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "techempower.stpl", rm_whitespace = true)]
|
#[template(path = "techempower.stpl", rm_whitespace = true)]
|
||||||
struct Techempower {
|
struct Techempower {
|
||||||
items: Vec<Fortune>,
|
items: Vec<Fortune>,
|
||||||
|
@ -182,7 +182,7 @@ fn test_techempower() {
|
||||||
assert_render("techempower", Techempower { items });
|
assert_render("techempower", Techempower { items });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "rm_whitespace.stpl")]
|
#[template(path = "rm_whitespace.stpl")]
|
||||||
#[template(rm_whitespace = true)]
|
#[template(rm_whitespace = true)]
|
||||||
struct RmWhitespace<'a, 'b> {
|
struct RmWhitespace<'a, 'b> {
|
||||||
|
@ -199,7 +199,7 @@ fn test_rm_whitespace() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "comment.stpl")]
|
#[template(path = "comment.stpl")]
|
||||||
struct Comment {}
|
struct Comment {}
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ fn test_comment() {
|
||||||
assert_render("comment", Comment {})
|
assert_render("comment", Comment {})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "rust_macro.stpl", rm_whitespace = true)]
|
#[template(path = "rust_macro.stpl", rm_whitespace = true)]
|
||||||
struct RustMacro {
|
struct RustMacro {
|
||||||
value: Option<i32>,
|
value: Option<i32>,
|
||||||
|
@ -219,7 +219,7 @@ fn test_rust_macro() {
|
||||||
assert_render("rust_macro", RustMacro { value: Some(10) });
|
assert_render("rust_macro", RustMacro { value: Some(10) });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "formatting.stpl", escape = false)]
|
#[template(path = "formatting.stpl", escape = false)]
|
||||||
struct Formatting;
|
struct Formatting;
|
||||||
|
|
||||||
|
@ -228,7 +228,7 @@ fn test_formatting() {
|
||||||
assert_render("formatting", Formatting);
|
assert_render("formatting", Formatting);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "filter.stpl")]
|
#[template(path = "filter.stpl")]
|
||||||
struct Filter<'a> {
|
struct Filter<'a> {
|
||||||
message: &'a str,
|
message: &'a str,
|
||||||
|
@ -239,7 +239,7 @@ fn test_filter() {
|
||||||
assert_render("filter", Filter { message: "hello" });
|
assert_render("filter", Filter { message: "hello" });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "filter2.stpl")]
|
#[template(path = "filter2.stpl")]
|
||||||
struct Filter2;
|
struct Filter2;
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ fn test_filter2() {
|
||||||
assert_render("filter2", Filter2);
|
assert_render("filter2", Filter2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "truncate-filter.stpl")]
|
#[template(path = "truncate-filter.stpl")]
|
||||||
struct TruncateFilter;
|
struct TruncateFilter;
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ struct TruncateFilter;
|
||||||
fn test_truncate_filter() {
|
fn test_truncate_filter() {
|
||||||
assert_render("truncate-filter", TruncateFilter);
|
assert_render("truncate-filter", TruncateFilter);
|
||||||
}
|
}
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "json-filter.stpl")]
|
#[template(path = "json-filter.stpl")]
|
||||||
struct JsonFilter {
|
struct JsonFilter {
|
||||||
data: serde_json::Value,
|
data: serde_json::Value,
|
||||||
|
@ -280,7 +280,7 @@ fn test_json_filter() {
|
||||||
mod unix {
|
mod unix {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "include-nest.stpl")]
|
#[template(path = "include-nest.stpl")]
|
||||||
struct IncludeNest<'a> {
|
struct IncludeNest<'a> {
|
||||||
s: &'a str,
|
s: &'a str,
|
||||||
|
@ -291,7 +291,7 @@ mod unix {
|
||||||
assert_render("include-nest", IncludeNest { s: "foo" });
|
assert_render("include-nest", IncludeNest { s: "foo" });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateOnce)]
|
#[derive(RenderOnce)]
|
||||||
#[template(path = "include_rust.stpl")]
|
#[template(path = "include_rust.stpl")]
|
||||||
struct IncludeRust {
|
struct IncludeRust {
|
||||||
value: usize,
|
value: usize,
|
||||||
|
|
|
@ -4,16 +4,16 @@
|
||||||
//!
|
//!
|
||||||
//! This crate contains utilities for rendering sailfish template.
|
//! This crate contains utilities for rendering sailfish template.
|
||||||
//! If you want to use sailfish templates, import `sailfish-macros` crate and use
|
//! 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
|
//! 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
|
//! if you want to render custom data inside templates, you must implement
|
||||||
//! `runtime::Render` trait for that type.
|
//! `runtime::Render` trait for that type.
|
||||||
//!
|
//!
|
||||||
//! ```ignore
|
//! ```ignore
|
||||||
//! use sailfish::TemplateOnce;
|
//! use sailfish::RenderOnce;
|
||||||
//!
|
//!
|
||||||
//! #[derive(TemplateOnce)]
|
//! #[derive(RenderOnce)]
|
||||||
//! #[template(path = "hello.stpl")]
|
//! #[template(path = "hello.stpl")]
|
||||||
//! struct HelloTemplate {
|
//! struct HelloTemplate {
|
||||||
//! messages: Vec<String>
|
//! messages: Vec<String>
|
||||||
|
@ -35,92 +35,10 @@
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![allow(clippy::redundant_closure)]
|
#![allow(clippy::redundant_closure)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![feature(specialization)]
|
|
||||||
|
|
||||||
pub mod runtime;
|
pub mod runtime;
|
||||||
|
|
||||||
use runtime::Buffer;
|
pub use runtime::{Buffer, Render, RenderError, RenderOnce, RenderResult};
|
||||||
pub use runtime::{RenderError, RenderResult};
|
|
||||||
#[cfg(feature = "derive")]
|
#[cfg(feature = "derive")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
|
||||||
pub use sailfish_macros::TemplateOnce;
|
pub use sailfish_macros::{Render, RenderOnce};
|
||||||
|
|
||||||
/// 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 {}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, MutexGuard, RwLockReadGuard, RwLockWriteGuard};
|
use std::sync::{Arc, MutexGuard, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
|
use crate::runtime::SizeHint;
|
||||||
|
|
||||||
use super::buffer::Buffer;
|
use super::buffer::Buffer;
|
||||||
use super::escape;
|
use super::escape;
|
||||||
|
|
||||||
|
@ -57,6 +59,33 @@ pub trait Render {
|
||||||
/// See [`Render`] for more information.
|
/// See [`Render`] for more information.
|
||||||
pub trait RenderOnce: Sized {
|
pub trait RenderOnce: Sized {
|
||||||
/// render to `Buffer` without escaping
|
/// 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>;
|
fn render_once(self, b: &mut Buffer) -> Result<(), RenderError>;
|
||||||
|
|
||||||
/// render to `Buffer` with HTML escaping
|
/// render to `Buffer` with HTML escaping
|
||||||
|
@ -67,6 +96,26 @@ pub trait RenderOnce: Sized {
|
||||||
escape::escape_to_buf(tmp.as_str(), b);
|
escape::escape_to_buf(tmp.as_str(), b);
|
||||||
Ok(())
|
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
|
// impl<'a, T: ?Sized> Render for &'a T
|
||||||
|
@ -86,10 +135,12 @@ impl<T> RenderOnce for T
|
||||||
where
|
where
|
||||||
T: Render,
|
T: Render,
|
||||||
{
|
{
|
||||||
|
#[inline]
|
||||||
fn render_once(self, b: &mut Buffer) -> Result<(), RenderError> {
|
fn render_once(self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
self.render(b)
|
self.render(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn render_once_escaped(self, b: &mut Buffer) -> Result<(), RenderError> {
|
fn render_once_escaped(self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
self.render_escaped(b)
|
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 T);
|
||||||
render_deref!(['a, T: Render + ?Sized] [] &'a mut 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] [] Rc<T>);
|
||||||
render_deref!([T: Render + ?Sized] [] Arc<T>);
|
render_deref!([T: Render + ?Sized] [] Arc<T>);
|
||||||
render_deref!(['a, T: Render + ToOwned + ?Sized] [] Cow<'a, 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>;
|
pub type RenderResult = Result<String, RenderError>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in New Issue