Merge branch 'master' into stable
This commit is contained in:
commit
5e02026459
|
@ -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]]
|
||||
|
|
28
README.md
28
README.md
|
@ -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).
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 |
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
|
@ -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 %><% ; %>
|
||||
```
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue