Merge branch 'master' into stable

This commit is contained in:
Kogia-sima 2020-07-04 06:17:21 +09:00
commit 5e02026459
26 changed files with 865 additions and 361 deletions

20
Cargo.lock generated
View File

@ -661,11 +661,11 @@ dependencies = [
[[package]]
name = "integration-tests"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish 0.1.1",
"sailfish-macros 0.1.1",
"sailfish 0.1.2",
"sailfish-macros 0.1.2",
"trybuild 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1040,7 +1040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sailfish"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1048,7 +1048,7 @@ dependencies = [
[[package]]
name = "sailfish-compiler"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1060,20 +1060,20 @@ dependencies = [
[[package]]
name = "sailfish-examples"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"actix-rt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish 0.1.1",
"sailfish-macros 0.1.1",
"sailfish 0.1.2",
"sailfish-macros 0.1.2",
]
[[package]]
name = "sailfish-macros"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish-compiler 0.1.1",
"sailfish-compiler 0.1.2",
]
[[package]]

View File

@ -19,7 +19,7 @@ Simple, small, and extremely fast template engine for Rust
- Relatively small number of dependencies (<15 crates in total)
- Extremely fast (See [benchmarks](./benches/README.md))
- Better error message
- Template rendering is always type-safe because templates are statically compiled.
- Template rendering NEVER fails unless you explicitly return error.
- Syntax highlighting support ([vscode](./syntax/vscode), [vim](./syntax/vim))
- Automatically re-compile sources when template file is updated.
- Works on Rust 1.42 or later
@ -30,17 +30,18 @@ Dependencies:
```toml
[dependencies]
sailfish = "0.1.1"
sailfish-macros = "0.1.1"
sailfish = "0.1.2"
sailfish-macros = "0.1.2"
```
Template file (templates/hello.stpl):
```erb
<DOCTYPE! html>
<html>
<body>
<%= content %>
<% for msg in &messages { %>
<div><%= msg %></div>
<% } %>
</body>
</html>
```
@ -55,12 +56,15 @@ use sailfish::TemplateOnce;
#[derive(TemplateOnce)]
#[template(path = "hello.stpl")]
struct Hello {
content: String
struct HelloTemplate {
messages: Vec<String>
}
fn main() {
println!("{}", Hello { content: String::from("Hello, world!") }.render_once().unwrap());
let ctx = HelloTemplate {
messages: vec![String::from("foo"), String::from("bar")]
}
println!("{}", ctx.render_once().unwrap());
}
```
@ -71,13 +75,12 @@ You can find more examples in [examples](./examples) directory.
- `Template` trait ([RFC](https://github.com/Kogia-sima/sailfish/issues/3))
- Template inheritance (block, partials, etc.)
- Whitespace suppressing
- Filters ([RFC](https://github.com/Kogia-sima/sailfish/issues/2))
- Dynamic template compilation ([RFC](https://github.com/Kogia-sima/sailfish/issues/1))
- `format_templates!(fmt, args..)` macro
## 👤 Author
:jp: **Ryohei Machida**
🇯🇵 **Ryohei Machida**
* Github: [@Kogia-sima](https://github.com/Kogia-sima)
@ -85,13 +88,14 @@ You can find more examples in [examples](./examples) directory.
Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/Kogia-sima/sailfish/issues).
Since sailfish is an immature library, there are many [planned features](https://github.com/Kogia-sima/sailfish/labels/Type%3A%20RFC) that is on a stage of RFC. Please leave a comment if you have an idea about its design!
Also I welcome any pull requests to improve sailfish! Find issue with [Status: PR Welcome](https://github.com/Kogia-sima/sailfish/issues?q=is%3Aissue+is%3Aopen+label%3A%22Status%3A+PR+Welcome%22) label, and [let's create a new pull request](https://github.com/Kogia-sima/sailfish/pulls)!
## Show your support
Give a ⭐️ if this project helped you!
## 📝 License
Copyright © 2020 [Ryohei Machida](https://github.com/Kogia-sima).

740
benches/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ publish = false
[dependencies]
askama = { git = "https://github.com/djc/askama" }
criterion = "0.3"
criterion = { version = "0.3", features = ["real_blackbox"] }
fomat-macros = { git = "https://github.com/krdln/fomat-macros" }
handlebars = { git = "https://github.com/sunng87/handlebars-rust" }
horrorshow = { git = "https://github.com/Stebalien/horrorshow-rs" }
@ -23,7 +23,7 @@ serde_derive = "1"
serde_json = "1"
serde_yaml = "0.8"
tera = { git = "https://github.com/Keats/tera" }
yarte = { git = "https://github.com/botika/yarte", features = ["bytes_buff"] }
yarte = { git = "https://github.com/botika/yarte", features = ["bytes-buf", "fixed"] }
[build-dependencies]
ructe = { git = "https://github.com/kaj/ructe" }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -10,7 +10,7 @@ pub fn big_table(b: &mut criterion::Bencher<'_>, size: &usize) {
table.push(inner);
}
let t = BigTable { table };
b.iter(|| t.call(109915).unwrap());
b.iter(|| t.call(109915));
}
#[derive(TemplateBytes)]
@ -42,7 +42,7 @@ pub fn teams(b: &mut criterion::Bencher<'_>) {
},
],
};
b.iter(|| t.call(239).unwrap());
b.iter(|| t.call(239));
}
#[derive(TemplateBytes)]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -40,9 +40,9 @@ Define the template struct to be rendered:
```rust
#[derive(TemplateOnce)] // automatically implement `TemplateOnce` trait
#[template(path = "hello.stpl")] // specify the path to template
struct HelloTemplate<'a> {
struct HelloTemplate {
// data to be passed to the template
messages: &'a [String],
messages: Vec<String>,
}
```
@ -51,7 +51,7 @@ And render the data with `render_once()` method.
```rust
fn main() {
let ctx = HelloTemplate {
messages: &[String::from("foo"), String::from("bar")];
messages: vec![String::from("foo"), String::from("bar")];
}
// Now render templates with given data

View File

@ -4,10 +4,13 @@ In order to use sailfish templates, you have add two dependencies in your `Cargo
```toml
[dependencies]
sailfish = "0.1.1"
sailfish-macros = "0.1.1"
sailfish = "0.1.2"
sailfish-macros = "0.1.2"
```
`sailfish` crate contains runtime for rendering contents, and `sailfish-macros` serves you derive macros to compile and import the template files.
These crates are separated so that Rust compiler can compile them independently. This separation makes your compilation faster!
!!! Warning
Make sure that the `sailfish-macros` version is larger than `sailfish`, otherwise the compilation may fail.

View File

@ -47,7 +47,7 @@ If a key is specified in both configuration file and derive options, then the va
### Configuration file format
Configuration files are written in the YAML 1.2 format. The default configuration is described below.
Configuration files are written in the YAML 1.2 format. Here is the default configuration.
```
template_dir: "templates"

View File

@ -0,0 +1,48 @@
# Includes
You can also include another template inside templates using `include!` macro.
Consider the following example.
- `templates/header.stpl`
```html
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="format-detection" content="telephone=no">
<link rel="icon" type="image/x-icon" href="favicon.ico">
```
- `templates/index.stpl`
```html
<html>
<head>
<% include!("./header.stpl"); %>
</head>
<body>
Main contents
</body>
</html>
```
Then you can see the `header.stpl` is embedded in the output.
```html
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="format-detection" content="telephone=no">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
Main contents
</body>
</html>
```
Like [`std::include!`](https://doc.rust-lang.org/std/macro.include.html) macro in Rust, the provided path is interpreted as a relative path to the current template file.
!!! Warning
The path format is platform-specific. You must use `\` character as a separator on Windows.

View File

@ -0,0 +1,72 @@
# Tags
## Code block
You can write Rust statement inside `<% %>` tag.
```ejs
<% let mut total = 0; %>
<% for elem in arr.iter().filter(|e| e.is_valid()) { %>
<% total += elem.value() as u64; %>
<% dbg!(total); %>
<% if total > 100 { break; } %>
Printed until the total value exceeds 100.
<% } %>
```
!!! Note
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.
```text
<% /* Tag does not ends at %>! */ %>
```
If you need to simply render `<%` character, you can escape it, or use evaluation block (described below).
```text
<%% is converted into <%= "<%" %> character.
```
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)
## Evaluation block
Rust expression inside `<%= %>` tag is evaluated and the result will be rendered.
```ejs
<%# The following code simple renders `3` %>
<% let a = 1; %><%= a + 2 %>
```
If the result contains `&"'<>` characters, sailfish replaces these characters with the equivalent html.
If you want to render the results without escaping, you can use `<%- %>` tag or [configure sailfish to not escape by default](../options.md). For example,
```ejs
<div>
<%- "<h1>Hello, World!</h1>" %>
</div>
```
This template results in the following output.
```ejs
<div>
<h1>Hello, World!</h1>
</div>
```
!!! 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.
```
<% let result = %><%= 1 %><% ; %>
```

View File

@ -27,6 +27,7 @@ theme:
# Extensions
markdown_extensions:
- admonition
- codehilite
- footnotes
@ -47,3 +48,5 @@ nav:
- 'Configuration': 'options.md'
- 'Syntax':
- 'Overview': 'syntax/overview.md'
- 'Tags': 'syntax/tags.md'
- 'Includes': 'syntax/includes.md'

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-examples"
version = "0.1.1"
version = "0.1.2"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
edition = "2018"
publish = false

View File

@ -1,6 +1,6 @@
[package]
name = "integration-tests"
version = "0.1.1"
version = "0.1.2"
authors = ["Kogia-sima <orcinus4627@gmail.com>"]
edition = "2018"
publish = false

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-compiler"
version = "0.1.1"
version = "0.1.2"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"

View File

@ -264,23 +264,27 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
let tokens = quote! {
impl #impl_generics sailfish::TemplateOnce for #name #ty_generics #where_clause {
fn render_once(self) -> sailfish::runtime::RenderResult {
fn render_once_to_string(self, buf: &mut String) -> Result<(), sailfish::runtime::RenderError> {
#include_bytes_seq;
use sailfish::runtime as sfrt;
use sfrt::RenderInternal as _;
static SIZE_HINT: sfrt::SizeHint = sfrt::SizeHint::new();
let _size_hint = SIZE_HINT.get();
let mut _ctx = sfrt::Context {
buf: sfrt::Buffer::with_capacity(_size_hint)
buf: sfrt::Buffer::from(std::mem::take(buf))
};
let _size_hint = SIZE_HINT.get();
_ctx.buf.reserve(_size_hint);
let #name { #field_names } = self;
include!(#output_file_string);
SIZE_HINT.update(_ctx.buf.len());
_ctx.into_result()
*buf = _ctx.buf.into_string();
Ok(())
}
}
};

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-macros"
version = "0.1.1"
version = "0.1.2"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"
@ -29,6 +29,6 @@ proc-macro2 = "1.0.11"
[dependencies.sailfish-compiler]
path = "../sailfish-compiler"
version = "0.1.1"
version = "0.1.2"
default-features = false
features = ["procmacro"]

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish"
version = "0.1.1"
version = "0.1.2"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"

View File

@ -41,8 +41,20 @@ pub mod runtime;
pub use runtime::{RenderError, RenderResult};
/// Template that can be rendered with consuming itself.
pub trait TemplateOnce {
fn render_once(self) -> runtime::RenderResult;
pub trait TemplateOnce: Sized {
/// Render the template and return the rendering result as `RenderResult`
///
/// This method never returns `Err`, unless you manually implement `Render` trait
/// for custom type and return `Err` inside it.
#[inline]
fn render_once(self) -> runtime::RenderResult {
let mut buf = String::new();
self.render_once_to_string(&mut buf)?;
Ok(buf)
}
/// Render the template and append the result to `buf`.
fn render_once_to_string(self, buf: &mut String) -> Result<(), RenderError>;
}
/// WIP

View File

@ -110,6 +110,9 @@ impl Buffer {
self.len = 0;
}
/// Converts a `Buffer` into a `String`.
///
/// This consumes the `Buffer`, so we do not need to copy its contents.
#[inline]
pub fn into_string(self) -> String {
let buf = ManuallyDrop::new(self);

View File

@ -33,13 +33,13 @@ const ESCAPED_LEN: usize = 5;
static FN: AtomicPtr<()> = AtomicPtr::new(escape as FnRaw);
#[cfg(target_feature = "avx2")]
pub fn escape(feed: &str, buf: &mut Buffer) {
unsafe { avx2::escape(buf, feed.as_bytes()) }
#[inline]
fn escape(feed: &str, buf: &mut Buffer) {
unsafe { avx2::escape(feed, buf) }
}
/// default escape function
#[cfg(not(target_feature = "avx2"))]
pub fn escape(feed: &str, buf: &mut Buffer) {
fn escape(feed: &str, buf: &mut Buffer) {
let fun = if is_x86_feature_detected!("avx2") {
avx2::escape
} else if is_x86_feature_detected!("sse2") {
@ -52,13 +52,19 @@ pub fn escape(feed: &str, buf: &mut Buffer) {
unsafe { fun(feed, buf) };
}
pub fn register_escape_fn(fun: fn(&str, &mut Buffer)) {
FN.store(fun as FnRaw, Ordering::Relaxed);
}
/// Change the default escape function
#[deprecated(since = "0.1.2", note = "This function does not anything any more")]
pub fn register_escape_fn(_fun: fn(&str, &mut Buffer)) {}
#[inline]
pub(crate) fn escape_to_buf(feed: &str, buf: &mut Buffer) {
unsafe {
if feed.len() < 16 {
let start_ptr = feed.as_ptr();
let end_ptr = start_ptr.add(feed.len());
naive::escape(buf, start_ptr, start_ptr, end_ptr);
return;
}
let fun = FN.load(Ordering::Relaxed);
mem::transmute::<FnRaw, fn(&str, &mut Buffer)>(fun)(feed, buf);
}

View File

@ -189,6 +189,71 @@ unsafe fn write_u64(mut n: u64, buf: *mut u8) -> usize {
}
}
unsafe fn write_u128(n: u128, buf: *mut u8) -> usize {
if n <= std::u64::MAX as u128 {
write_u64(n as u64, buf)
} else if n < 100000000000000000000000000000000 {
let a0 = (n / 10000000000000000) as u64;
let a1 = (n % 10000000000000000) as u64;
let b0 = (a1 / 100000000) as u32;
let b1 = (a1 / 100000000) as u32;
let c0 = (b0 / 10000) as u16;
let c1 = (b0 % 10000) as u16;
let c2 = (b1 / 10000) as u16;
let c3 = (b1 % 10000) as u16;
let l = write_u64(a0, buf);
write_small_pad(c0, buf.add(l));
write_small_pad(c1, buf.add(l + 4));
write_small_pad(c2, buf.add(l + 8));
write_small_pad(c3, buf.add(l + 12));
l + 16
} else {
let a0 = (n / 100000000000000000000000000000000) as u32; // 1 to 3402823
let a1 = n % 100000000000000000000000000000000;
let b0 = (a1 / 10000000000000000) as u64;
let b1 = (a1 % 10000000000000000) as u64;
let c0 = (b0 / 100000000) as u32;
let c1 = (b0 % 100000000) as u32;
let c2 = (b1 / 100000000) as u32;
let c3 = (b1 % 100000000) as u32;
let d0 = (c0 / 10000) as u16;
let d1 = (c0 % 10000) as u16;
let d2 = (c1 / 10000) as u16;
let d3 = (c1 % 10000) as u16;
let d4 = (c2 / 10000) as u16;
let d5 = (c2 % 10000) as u16;
let d6 = (c3 / 10000) as u16;
let d7 = (c3 % 10000) as u16;
let l = if a0 < 10000 {
write_small(a0 as u16, buf)
} else {
let b0 = (a0 / 10000) as u16;
let b1 = (a0 % 10000) as u16;
let l = write_small(b0, buf);
write_small_pad(b1, buf.add(l));
l + 4
};
write_small_pad(d0, buf.add(l));
write_small_pad(d1, buf.add(l + 4));
write_small_pad(d2, buf.add(l + 8));
write_small_pad(d3, buf.add(l + 12));
write_small_pad(d4, buf.add(l + 16));
write_small_pad(d5, buf.add(l + 20));
write_small_pad(d6, buf.add(l + 24));
write_small_pad(d7, buf.add(l + 28));
l + 32
}
}
pub trait Integer {
const MAX_LEN: usize;
unsafe fn write_to(self, buf: *mut u8) -> usize;
@ -227,6 +292,7 @@ impl_integer!(u8, i8, u8, write_u8, 3);
impl_integer!(u16, i16, u16, write_u16, 5);
impl_integer!(u32, i32, u32, write_u32, 10);
impl_integer!(u64, i64, u64, write_u64, 20);
impl_integer!(u128, i128, u128, write_u128, 39);
#[cfg(target_pointer_width = "16")]
impl_integer!(usize, isize, u16, write_u16, 6);
@ -282,17 +348,21 @@ mod tests {
fn $name() {
use super::Integer;
unsafe fn test_write(val: $type, buf: &mut Vec<u8>) {
let l = val.write_to(buf.as_mut_ptr());
buf.set_len(l);
assert_eq!(
std::str::from_utf8_unchecked(&*buf),
format!("{}", val)
);
}
let mut buf = Vec::with_capacity(<$type>::MAX_LEN);
$(
unsafe {
let l = ($value as $type).write_to(buf.as_mut_ptr());
buf.set_len(l);
assert_eq!(
std::str::from_utf8_unchecked(&*buf),
format!("{}", $value as $type)
);
}
)*
unsafe {
$(
test_write($value as $type, &mut buf);
)*
}
}
}
}
@ -350,8 +420,93 @@ mod tests {
18446744073709551615
);
make_test!(
test_u128,
u128,
0,
9,
10,
99,
100,
999,
1000,
9999,
10000,
99999,
100000,
999999,
1000000,
9999999,
10000000,
99999999,
100000000,
999999999,
1000000000,
9999999999,
10000000000,
99999999999,
100000000000,
999999999999,
1000000000000,
9999999999999,
10000000000000,
99999999999999,
100000000000000,
999999999999999,
1000000000000000,
9999999999999999,
10000000000000000,
99999999999999999,
100000000000000000,
999999999999999999,
1000000000000000000,
9999999999999999999,
10000000000000000000,
99999999999999999999,
100000000000000000000,
999999999999999999999,
1000000000000000000000,
9999999999999999999999,
10000000000000000000000,
99999999999999999999999,
100000000000000000000000,
100000000000000000000000,
999999999999999999999999,
1000000000000000000000000,
9999999999999999999999999,
10000000000000000000000000,
99999999999999999999999999,
100000000000000000000000000,
999999999999999999999999999,
1000000000000000000000000000,
9999999999999999999999999999,
10000000000000000000000000000,
99999999999999999999999999999,
100000000000000000000000000000,
999999999999999999999999999999,
1000000000000000000000000000000,
9999999999999999999999999999999,
10000000000000000000000000000000,
99999999999999999999999999999999,
100000000000000000000000000000000,
999999999999999999999999999999999,
1000000000000000000000000000000000,
9999999999999999999999999999999999,
10000000000000000000000000000000000,
99999999999999999999999999999999999,
100000000000000000000000000000000000,
999999999999999999999999999999999999,
1000000000000000000000000000000000000,
9999999999999999999999999999999999999,
10000000000000000000000000000000000000,
99999999999999999999999999999999999999,
100000000000000000000000000000000000000,
340282366920938463463374607431768211455
);
make_test!(test_i8, i8, std::i8::MIN, std::i8::MAX);
make_test!(test_i16, i16, std::i16::MIN, std::i16::MAX);
make_test!(test_i32, i32, std::i32::MIN, std::i32::MAX);
make_test!(test_i64, i64, std::i64::MIN, std::i64::MAX);
make_test!(test_i128, i128, std::i128::MIN, std::i128::MAX);
}

View File

@ -16,29 +16,51 @@ use std::fmt;
#[doc(hidden)]
pub use crate::{render, render_escaped, render_noop, render_text};
#[derive(Clone, Debug)]
enum RenderErrorKind {
Msg(String),
Fmt(fmt::Error),
}
/// The error type which is returned from template function
#[derive(Clone, Debug)]
pub struct RenderError {
// currently RenderError simply wraps the fmt::Error
inner: fmt::Error,
kind: RenderErrorKind,
}
impl RenderError {
pub fn new(msg: &str) -> Self {
Self {
kind: RenderErrorKind::Msg(msg.to_owned()),
}
}
}
impl fmt::Display for RenderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.inner.fmt(f)
match self.kind {
RenderErrorKind::Msg(ref s) => f.write_str(&**s),
RenderErrorKind::Fmt(ref e) => fmt::Display::fmt(e, f),
}
}
}
impl std::error::Error for RenderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.inner)
match self.kind {
RenderErrorKind::Msg(_) => None,
RenderErrorKind::Fmt(ref e) => Some(e),
}
}
}
impl From<fmt::Error> for RenderError {
#[inline]
fn from(other: fmt::Error) -> Self {
Self { inner: other }
Self {
kind: RenderErrorKind::Fmt(other),
}
}
}

View File

@ -4,6 +4,24 @@ use super::buffer::Buffer;
use super::{escape, RenderError};
/// types which can be rendered inside buffer block (`<%= %>`)
///
/// If you want to render the custom data, you must implement this trait and specify
/// the behaviour.
///
/// # Examples
///
/// ```
/// use sailfish::runtime::{Buffer, Render, RenderError};
///
/// struct MyU64(u64);
///
/// impl Render for MyU64 {
/// #[inline]
/// fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
/// self.0.render(b)
/// }
/// }
/// ```
pub trait Render {
fn render(&self, b: &mut Buffer) -> Result<(), RenderError>;
@ -154,7 +172,7 @@ macro_rules! render_int {
}
}
render_int!(u8, u16, u32, u64, i8, i16, i32, i64, usize, isize);
render_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
impl Render for f32 {
#[inline]

View File

@ -1,6 +1,6 @@
use std::sync::atomic::{AtomicUsize, Ordering};
/// Dynamic size hint
/// Dynamically updated size hint
#[derive(Debug, Default)]
pub struct SizeHint {
value: AtomicUsize,