Merge branch 'master' into stable
This commit is contained in:
commit
c809027152
File diff suppressed because it is too large
Load Diff
|
@ -3,11 +3,12 @@ members = [
|
||||||
"sailfish",
|
"sailfish",
|
||||||
"sailfish-compiler",
|
"sailfish-compiler",
|
||||||
"sailfish-macros",
|
"sailfish-macros",
|
||||||
"examples",
|
"sailfish-tests/integration-tests"
|
||||||
"integration-tests"
|
|
||||||
]
|
]
|
||||||
exclude = [
|
exclude = [
|
||||||
"benches"
|
"sailfish-tests/fuzzing-tests",
|
||||||
|
"benches",
|
||||||
|
"examples"
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.dev.package.syn]
|
[profile.dev.package.syn]
|
||||||
|
|
|
@ -30,8 +30,8 @@ Dependencies:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sailfish = "0.1.3"
|
sailfish = "0.2.0"
|
||||||
sailfish-macros = "0.1.3"
|
sailfish-macros = "0.2.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Template file (templates/hello.stpl):
|
Template file (templates/hello.stpl):
|
||||||
|
|
|
@ -188,9 +188,10 @@ checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "benches"
|
name = "benches"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
|
"bytes",
|
||||||
"criterion",
|
"criterion",
|
||||||
"fomat-macros",
|
"fomat-macros",
|
||||||
"handlebars",
|
"handlebars",
|
||||||
|
@ -207,7 +208,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"tera",
|
"tera",
|
||||||
"v_htmlescape",
|
"v_htmlescape 0.9.1",
|
||||||
"yarte",
|
"yarte",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -271,6 +272,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "buf-min"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c4ab58c23b5bf0d27713787d5dcd5f62c2260ca172d5af24b7de706bcc2897f"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.4.0"
|
version = "3.4.0"
|
||||||
|
@ -1800,7 +1810,7 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sailfish"
|
name = "sailfish"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoap",
|
"itoap",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -1809,7 +1819,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sailfish-compiler"
|
name = "sailfish-compiler"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"proc-macro2 1.0.18",
|
"proc-macro2 1.0.18",
|
||||||
|
@ -1820,7 +1830,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sailfish-macros"
|
name = "sailfish-macros"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.18",
|
"proc-macro2 1.0.18",
|
||||||
"sailfish-compiler",
|
"sailfish-compiler",
|
||||||
|
@ -2286,6 +2296,16 @@ dependencies = [
|
||||||
"v_escape_derive",
|
"v_escape_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "v_escape"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b66158ce426982197fd44266d68125fd4000f1d42f5ee33ef02b500b4b6b0024"
|
||||||
|
dependencies = [
|
||||||
|
"buf-min",
|
||||||
|
"v_escape_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "v_escape_derive"
|
name = "v_escape_derive"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
|
@ -2315,7 +2335,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37981da7d2bd82edc21de370f4d7b010360c8590f70c9f76f5df20e780dc49f2"
|
checksum = "37981da7d2bd82edc21de370f4d7b010360c8590f70c9f76f5df20e780dc49f2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"v_escape",
|
"v_escape 0.11.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "v_htmlescape"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5fd25529cb2f78527b5ee507bcfb357b26d057b5e480853c26d49a4ead5c629"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"v_escape 0.12.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2489,18 +2519,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yarte"
|
name = "yarte"
|
||||||
version = "0.11.3"
|
version = "0.12.0"
|
||||||
source = "git+https://github.com/botika/yarte#cf0bf4504c4d8d8a6346d3584e0331bfdd8f78f5"
|
source = "git+https://github.com/botika/yarte#a39f49d5e2f7826b1be492bca56cd79342369fe3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"buf-min",
|
||||||
"yarte_derive",
|
"yarte_derive",
|
||||||
"yarte_helpers",
|
"yarte_helpers",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yarte_codegen"
|
name = "yarte_codegen"
|
||||||
version = "0.11.3"
|
version = "0.12.0"
|
||||||
source = "git+https://github.com/botika/yarte#cf0bf4504c4d8d8a6346d3584e0331bfdd8f78f5"
|
source = "git+https://github.com/botika/yarte#a39f49d5e2f7826b1be492bca56cd79342369fe3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.18",
|
"proc-macro2 1.0.18",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
|
@ -2511,8 +2541,8 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yarte_derive"
|
name = "yarte_derive"
|
||||||
version = "0.11.3"
|
version = "0.12.0"
|
||||||
source = "git+https://github.com/botika/yarte#cf0bf4504c4d8d8a6346d3584e0331bfdd8f78f5"
|
source = "git+https://github.com/botika/yarte#a39f49d5e2f7826b1be492bca56cd79342369fe3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bat",
|
"bat",
|
||||||
"proc-macro2 1.0.18",
|
"proc-macro2 1.0.18",
|
||||||
|
@ -2528,37 +2558,37 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yarte_helpers"
|
name = "yarte_helpers"
|
||||||
version = "0.11.3"
|
version = "0.12.0"
|
||||||
source = "git+https://github.com/botika/yarte#cf0bf4504c4d8d8a6346d3584e0331bfdd8f78f5"
|
source = "git+https://github.com/botika/yarte#a39f49d5e2f7826b1be492bca56cd79342369fe3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"buf-min",
|
||||||
"dtoa",
|
"dtoa",
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
"v_htmlescape",
|
"v_htmlescape 0.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yarte_hir"
|
name = "yarte_hir"
|
||||||
version = "0.11.3"
|
version = "0.12.0"
|
||||||
source = "git+https://github.com/botika/yarte#cf0bf4504c4d8d8a6346d3584e0331bfdd8f78f5"
|
source = "git+https://github.com/botika/yarte#a39f49d5e2f7826b1be492bca56cd79342369fe3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"proc-macro2 1.0.18",
|
"proc-macro2 1.0.18",
|
||||||
"quote 1.0.7",
|
"quote 1.0.7",
|
||||||
"syn 1.0.33",
|
"syn 1.0.33",
|
||||||
"v_eval",
|
"v_eval",
|
||||||
"v_htmlescape",
|
"v_htmlescape 0.10.0",
|
||||||
"yarte_helpers",
|
"yarte_helpers",
|
||||||
"yarte_parser",
|
"yarte_parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yarte_parser"
|
name = "yarte_parser"
|
||||||
version = "0.11.3"
|
version = "0.12.0"
|
||||||
source = "git+https://github.com/botika/yarte#cf0bf4504c4d8d8a6346d3584e0331bfdd8f78f5"
|
source = "git+https://github.com/botika/yarte#a39f49d5e2f7826b1be492bca56cd79342369fe3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"annotate-snippets",
|
"annotate-snippets",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "benches"
|
name = "benches"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Dirkjan Ochtman <dirkjan@ochtman.nl>", "Ryohei Machida <orcinus4627@gmail.com>"]
|
authors = ["Dirkjan Ochtman <dirkjan@ochtman.nl>", "Ryohei Machida <orcinus4627@gmail.com>"]
|
||||||
build = "src/build.rs"
|
build = "src/build.rs"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
@ -25,6 +25,7 @@ serde_yaml = "0.8"
|
||||||
tera = { git = "https://github.com/Keats/tera" }
|
tera = { git = "https://github.com/Keats/tera" }
|
||||||
v_htmlescape = "0.9.1"
|
v_htmlescape = "0.9.1"
|
||||||
yarte = { git = "https://github.com/botika/yarte", features = ["bytes-buf", "fixed"] }
|
yarte = { git = "https://github.com/botika/yarte", features = ["bytes-buf", "fixed"] }
|
||||||
|
bytes = "0.5.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
ructe = { git = "https://github.com/kaj/ructe" }
|
ructe = { git = "https://github.com/kaj/ructe" }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use bytes::BytesMut;
|
||||||
use yarte::TemplateBytes;
|
use yarte::TemplateBytes;
|
||||||
|
|
||||||
pub fn big_table(b: &mut criterion::Bencher<'_>, size: &usize) {
|
pub fn big_table(b: &mut criterion::Bencher<'_>, size: &usize) {
|
||||||
|
@ -10,7 +11,7 @@ pub fn big_table(b: &mut criterion::Bencher<'_>, size: &usize) {
|
||||||
table.push(inner);
|
table.push(inner);
|
||||||
}
|
}
|
||||||
let t = BigTable { table };
|
let t = BigTable { table };
|
||||||
b.iter(|| t.call(109915));
|
b.iter(|| t.call::<BytesMut>(109915));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateBytes)]
|
#[derive(TemplateBytes)]
|
||||||
|
@ -42,7 +43,7 @@ pub fn teams(b: &mut criterion::Bencher<'_>) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
b.iter(|| t.call(239));
|
b.iter(|| t.call::<BytesMut>(239));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TemplateBytes)]
|
#[derive(TemplateBytes)]
|
||||||
|
|
|
@ -4,8 +4,8 @@ In order to use sailfish templates, you have add two dependencies in your `Cargo
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sailfish = "0.1.3"
|
sailfish = "0.2.0"
|
||||||
sailfish-macros = "0.1.3"
|
sailfish-macros = "0.2.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
`sailfish` crate contains runtime for rendering contents, and `sailfish-macros` serves you derive macros to compile and import the template files.
|
`sailfish` crate contains runtime for rendering contents, and `sailfish-macros` serves you derive macros to compile and import the template files.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "sailfish-examples"
|
name = "sailfish-examples"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"name": "<%= name %>",
|
|
||||||
"value": <%= value %>
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "sailfish-compiler"
|
name = "sailfish-compiler"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
||||||
description = "Really fast, intuitive template engine for Rust"
|
description = "Really fast, intuitive template engine for Rust"
|
||||||
homepage = "https://github.com/Kogia-sima/sailfish"
|
homepage = "https://github.com/Kogia-sima/sailfish"
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use syn::Block;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
|
@ -72,14 +74,13 @@ impl Compiler {
|
||||||
fs::create_dir_all(parent)
|
fs::create_dir_all(parent)
|
||||||
.chain_err(|| format!("Failed to save artifacts in {:?}", parent))?;
|
.chain_err(|| format!("Failed to save artifacts in {:?}", parent))?;
|
||||||
}
|
}
|
||||||
if output.exists() {
|
|
||||||
fs::remove_file(output)
|
|
||||||
.chain_err(|| format!("Failed to remove artifact {:?}", output))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let string = tsource.ast.into_token_stream().to_string();
|
let string = tsource.ast.into_token_stream().to_string();
|
||||||
fs::write(output, rustfmt_block(&*string).unwrap_or(string))
|
|
||||||
.chain_err(|| format!("Failed to save artifact in {:?}", output))?;
|
let mut f = fs::File::create(output)
|
||||||
|
.chain_err(|| format!("Failed to create artifact: {:?}", output))?;
|
||||||
|
writeln!(f, "{}", rustfmt_block(&*string).unwrap_or(string))
|
||||||
|
.chain_err(|| format!("Failed to write artifact into {:?}", output))?;
|
||||||
Ok(report)
|
Ok(report)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,4 +92,38 @@ impl Compiler {
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn compile_str(&self, input: &str) -> Result<String, Error> {
|
||||||
|
let dummy_path = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
|
||||||
|
let include_handler = Arc::new(|_: &Path| -> Result<Block, Error> {
|
||||||
|
Err(make_error!(
|
||||||
|
ErrorKind::AnalyzeError(
|
||||||
|
"include! macro is not allowed in inline template".to_owned()
|
||||||
|
),
|
||||||
|
source = input.to_owned()
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
let parser = Parser::new().delimiter(self.config.delimiter);
|
||||||
|
let translator = Translator::new().escape(self.config.escape);
|
||||||
|
let resolver = Resolver::new().include_handler(include_handler);
|
||||||
|
let optimizer = Optimizer::new().rm_whitespace(self.config.rm_whitespace);
|
||||||
|
|
||||||
|
let compile = || -> Result<String, Error> {
|
||||||
|
let stream = parser.parse(input);
|
||||||
|
let mut tsource = translator.translate(stream)?;
|
||||||
|
resolver.resolve(dummy_path, &mut tsource.ast)?;
|
||||||
|
|
||||||
|
optimizer.optimize(&mut tsource.ast);
|
||||||
|
Ok(tsource.ast.into_token_stream().to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
compile()
|
||||||
|
.chain_err(|| "Failed to compile template.")
|
||||||
|
.map_err(|mut e| {
|
||||||
|
e.source = Some(input.to_owned());
|
||||||
|
e
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ impl Parse for RenderTextMacroArgument {
|
||||||
fn get_rendertext_value(i: &ExprMacro) -> Option<String> {
|
fn get_rendertext_value(i: &ExprMacro) -> Option<String> {
|
||||||
let mut it = i.mac.path.segments.iter();
|
let mut it = i.mac.path.segments.iter();
|
||||||
|
|
||||||
if it.next().map_or(false, |s| s.ident == "sfrt")
|
if it.next().map_or(false, |s| s.ident == "__sf_rt")
|
||||||
&& it.next().map_or(false, |s| s.ident == "render_text")
|
&& it.next().map_or(false, |s| s.ident == "render_text")
|
||||||
&& it.next().is_none()
|
&& it.next().is_none()
|
||||||
{
|
{
|
||||||
|
@ -74,14 +74,14 @@ impl VisitMut for OptmizerImpl {
|
||||||
|
|
||||||
fl.body.stmts.remove(0);
|
fl.body.stmts.remove(0);
|
||||||
*fl.body.stmts.last_mut().unwrap() = syn::parse2(quote! {
|
*fl.body.stmts.last_mut().unwrap() = syn::parse2(quote! {
|
||||||
sfrt::render_text!(_ctx, #concat);
|
__sf_rt::render_text!(__sf_buf, #concat);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let new_expr = syn::parse2(quote! {{
|
let new_expr = syn::parse2(quote! {{
|
||||||
sfrt::render_text!(_ctx, #sf);
|
__sf_rt::render_text!(__sf_buf, #sf);
|
||||||
#fl;
|
#fl;
|
||||||
unsafe { _ctx.buf.set_len(_ctx.buf.len() - #sf_len); }
|
unsafe { __sf_buf._set_len(__sf_buf.len() - #sf_len); }
|
||||||
}})
|
}})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ impl VisitMut for OptmizerImpl {
|
||||||
buffer.push_str(line.trim_start());
|
buffer.push_str(line.trim_start());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i.mac.tokens = quote! { _ctx, #buffer };
|
i.mac.tokens = quote! { __sf_buf, #buffer };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,7 +289,7 @@ fn find_block_end(haystack: &str, delimiter: &str) -> Option<usize> {
|
||||||
b'/' => match remain.as_bytes().get(pos + 1).copied() {
|
b'/' => match remain.as_bytes().get(pos + 1).copied() {
|
||||||
Some(b'/') => unwrap_or_break!(find_comment_end(&remain[pos..])),
|
Some(b'/') => unwrap_or_break!(find_comment_end(&remain[pos..])),
|
||||||
Some(b'*') => unwrap_or_break!(find_block_comment_end(&remain[pos..])),
|
Some(b'*') => unwrap_or_break!(find_block_comment_end(&remain[pos..])),
|
||||||
_ => pos + 1,
|
_ => 1,
|
||||||
},
|
},
|
||||||
b'\"' => {
|
b'\"' => {
|
||||||
// check if the literal is a raw string
|
// check if the literal is a raw string
|
||||||
|
@ -311,7 +311,7 @@ fn find_block_end(haystack: &str, delimiter: &str) -> Option<usize> {
|
||||||
if remain[pos..].starts_with(delimiter) {
|
if remain[pos..].starts_with(delimiter) {
|
||||||
return Some(haystack.len() - remain.len() + pos + delimiter.len());
|
return Some(haystack.len() - remain.len() + pos + delimiter.len());
|
||||||
} else {
|
} else {
|
||||||
pos + 1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -200,6 +200,7 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find(|p| p.join("LICENSE").exists())
|
.find(|p| p.join("LICENSE").exists())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.join("sailfish-tests")
|
||||||
.join("integration-tests")
|
.join("integration-tests")
|
||||||
.join("tests")
|
.join("tests")
|
||||||
.join("fails")
|
.join("fails")
|
||||||
|
@ -269,23 +270,18 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
|
||||||
fn render_once_to_string(self, buf: &mut String) -> Result<(), sailfish::runtime::RenderError> {
|
fn render_once_to_string(self, buf: &mut String) -> Result<(), sailfish::runtime::RenderError> {
|
||||||
#include_bytes_seq;
|
#include_bytes_seq;
|
||||||
|
|
||||||
use sailfish::runtime as sfrt;
|
use sailfish::runtime as __sf_rt;
|
||||||
use sfrt::RenderInternal as _;
|
|
||||||
|
|
||||||
static SIZE_HINT: sfrt::SizeHint = sfrt::SizeHint::new();
|
static SIZE_HINT: __sf_rt::SizeHint = __sf_rt::SizeHint::new();
|
||||||
|
|
||||||
let mut _ctx = sfrt::Context {
|
let mut __sf_buf = __sf_rt::Buffer::from(std::mem::take(buf));
|
||||||
buf: sfrt::Buffer::from(std::mem::take(buf))
|
__sf_buf.reserve(SIZE_HINT.get());
|
||||||
};
|
|
||||||
|
|
||||||
let _size_hint = SIZE_HINT.get();
|
|
||||||
_ctx.buf.reserve(_size_hint);
|
|
||||||
|
|
||||||
let #name { #field_names } = self;
|
let #name { #field_names } = self;
|
||||||
include!(#output_file_string);
|
include!(#output_file_string);
|
||||||
|
|
||||||
SIZE_HINT.update(_ctx.buf.len());
|
SIZE_HINT.update(__sf_buf.len());
|
||||||
*buf = _ctx.buf.into_string();
|
*buf = __sf_buf.into_string();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,88 @@
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
|
use quote::ToTokens;
|
||||||
|
use syn::parse::{Parse, ParseStream as SynParseStream, Result as ParseResult};
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
use syn::{BinOp, Block, Expr};
|
||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
use crate::parser::{ParseStream, Token, TokenKind};
|
use crate::parser::{ParseStream, Token, TokenKind};
|
||||||
|
|
||||||
use syn::Block;
|
enum Filter {
|
||||||
|
Ident(syn::Ident),
|
||||||
|
Call(syn::ExprCall),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Spanned for Filter {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match *self {
|
||||||
|
Filter::Ident(ref i) => i.span(),
|
||||||
|
Filter::Call(ref c) => c.span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CodeBlock {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
expr: Box<Expr>,
|
||||||
|
filter: Option<Filter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for CodeBlock {
|
||||||
|
fn parse(s: SynParseStream) -> ParseResult<Self> {
|
||||||
|
let main = s.parse::<Expr>()?;
|
||||||
|
|
||||||
|
let code_block = match main {
|
||||||
|
Expr::Binary(b) if matches!(b.op, BinOp::BitOr(_)) => {
|
||||||
|
match *b.right {
|
||||||
|
Expr::Call(c) => {
|
||||||
|
if let Expr::Path(ref p) = *c.func {
|
||||||
|
if p.path.get_ident().is_some() {
|
||||||
|
CodeBlock {
|
||||||
|
expr: b.left,
|
||||||
|
filter: Some(Filter::Call(c)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
p,
|
||||||
|
"Invalid filter name",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if function in right side is not a path, fallback to
|
||||||
|
// normal evaluation block
|
||||||
|
CodeBlock {
|
||||||
|
expr: b.left,
|
||||||
|
filter: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Path(p) => {
|
||||||
|
if let Some(i) = p.path.get_ident() {
|
||||||
|
CodeBlock {
|
||||||
|
expr: b.left,
|
||||||
|
filter: Some(Filter::Ident(i.clone())),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
p,
|
||||||
|
"Invalid filter name",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(syn::Error::new_spanned(b, "Expected filter"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => CodeBlock {
|
||||||
|
expr: Box::new(main),
|
||||||
|
filter: None,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(code_block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SourceMapEntry {
|
pub struct SourceMapEntry {
|
||||||
|
@ -38,48 +117,6 @@ impl SourceMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TranslatedSource {
|
|
||||||
pub ast: Block,
|
|
||||||
pub source_map: SourceMap,
|
|
||||||
}
|
|
||||||
|
|
||||||
// translate tokens into Rust code
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct Translator {
|
|
||||||
escape: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Translator {
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { escape: true }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn escape(mut self, new: bool) -> Self {
|
|
||||||
self.escape = new;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn translate<'a>(
|
|
||||||
&self,
|
|
||||||
token_iter: ParseStream<'a>,
|
|
||||||
) -> Result<TranslatedSource, Error> {
|
|
||||||
let original_source = token_iter.original_source;
|
|
||||||
|
|
||||||
let mut source = String::with_capacity(original_source.len());
|
|
||||||
source.push_str("{\n");
|
|
||||||
let mut ps = SourceBuilder {
|
|
||||||
escape: self.escape,
|
|
||||||
source,
|
|
||||||
source_map: SourceMap::default(),
|
|
||||||
};
|
|
||||||
ps.feed_tokens(&*token_iter.into_vec()?);
|
|
||||||
|
|
||||||
Ok(ps.finalize()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SourceBuilder {
|
struct SourceBuilder {
|
||||||
escape: bool,
|
escape: bool,
|
||||||
source: String,
|
source: String,
|
||||||
|
@ -87,6 +124,18 @@ struct SourceBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceBuilder {
|
impl SourceBuilder {
|
||||||
|
fn new(escape: bool) -> SourceBuilder {
|
||||||
|
SourceBuilder {
|
||||||
|
escape,
|
||||||
|
source: String::from("{\n"),
|
||||||
|
source_map: SourceMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reserve(&mut self, additional: usize) {
|
||||||
|
self.source.reserve(additional);
|
||||||
|
}
|
||||||
|
|
||||||
fn write_token<'a>(&mut self, token: &Token<'a>) {
|
fn write_token<'a>(&mut self, token: &Token<'a>) {
|
||||||
let entry = SourceMapEntry {
|
let entry = SourceMapEntry {
|
||||||
original: token.offset(),
|
original: token.offset(),
|
||||||
|
@ -97,45 +146,96 @@ impl SourceBuilder {
|
||||||
self.source.push_str(token.as_str());
|
self.source.push_str(token.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_code<'a>(&mut self, token: &Token<'a>) {
|
fn write_code<'a>(&mut self, token: &Token<'a>) -> Result<(), Error> {
|
||||||
// TODO: automatically add missing tokens (e.g. ';', '{')
|
// TODO: automatically add missing tokens (e.g. ';', '{')
|
||||||
self.write_token(token);
|
self.write_token(token);
|
||||||
self.source.push_str("\n");
|
self.source.push_str("\n");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_text<'a>(&mut self, token: &Token<'a>) {
|
fn write_text<'a>(&mut self, token: &Token<'a>) -> Result<(), Error> {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
self.source.push_str("sfrt::render_text!(_ctx, ");
|
self.source.push_str("__sf_rt::render_text!(__sf_buf, ");
|
||||||
|
|
||||||
// write text token with Debug::fmt
|
// write text token with Debug::fmt
|
||||||
write!(self.source, "{:?}", token.as_str()).unwrap();
|
write!(self.source, "{:?}", token.as_str()).unwrap();
|
||||||
|
|
||||||
self.source.push_str(");\n");
|
self.source.push_str(");\n");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_buffered_code<'a>(&mut self, token: &Token<'a>, escape: bool) {
|
fn write_buffered_code<'a>(
|
||||||
|
&mut self,
|
||||||
|
token: &Token<'a>,
|
||||||
|
escape: bool,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// parse and split off filter
|
||||||
|
let code_block = syn::parse_str::<CodeBlock>(token.as_str()).map_err(|e| {
|
||||||
|
let span = e.span();
|
||||||
|
let mut err = make_error!(ErrorKind::RustSyntaxError(e));
|
||||||
|
err.offset = into_offset(token.as_str(), span).map(|p| token.offset() + p);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
let method = if self.escape && escape {
|
let method = if self.escape && escape {
|
||||||
"render_escaped"
|
"render_escaped"
|
||||||
} else {
|
} else {
|
||||||
"render"
|
"render"
|
||||||
};
|
};
|
||||||
|
|
||||||
self.source.push_str("sfrt::");
|
self.source.push_str("__sf_rt::");
|
||||||
self.source.push_str(method);
|
self.source.push_str(method);
|
||||||
self.source.push_str("!(_ctx, ");
|
self.source.push_str("!(__sf_buf, ");
|
||||||
self.write_token(token);
|
|
||||||
|
if let Some(filter) = code_block.filter {
|
||||||
|
let expr_str = code_block.expr.into_token_stream().to_string();
|
||||||
|
let (name, extra_args) = match filter {
|
||||||
|
Filter::Ident(i) => (i.to_string(), None),
|
||||||
|
Filter::Call(c) => (
|
||||||
|
c.func.into_token_stream().to_string(),
|
||||||
|
Some(c.args.into_token_stream().to_string()),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.source.push_str("sailfish::runtime::filter::");
|
||||||
|
self.source.push_str(&*name);
|
||||||
|
self.source.push_str("(");
|
||||||
|
|
||||||
|
// arguments to filter function
|
||||||
|
{
|
||||||
|
self.source.push_str("&(");
|
||||||
|
let entry = SourceMapEntry {
|
||||||
|
original: token.offset(),
|
||||||
|
new: self.source.len(),
|
||||||
|
length: expr_str.len(),
|
||||||
|
};
|
||||||
|
self.source_map.entries.push(entry);
|
||||||
|
self.source.push_str(&expr_str);
|
||||||
|
self.source.push_str(")");
|
||||||
|
|
||||||
|
if let Some(extra_args) = extra_args {
|
||||||
|
self.source.push_str(", ");
|
||||||
|
self.source.push_str(&*extra_args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.source.push_str(")");
|
||||||
|
} else {
|
||||||
|
self.write_token(token);
|
||||||
|
}
|
||||||
|
|
||||||
self.source.push_str(");\n");
|
self.source.push_str(");\n");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn feed_tokens(&mut self, token_iter: &[Token]) {
|
pub fn feed_tokens<'a>(&mut self, token_iter: ParseStream<'a>) -> Result<(), Error> {
|
||||||
let mut it = token_iter.iter().peekable();
|
let mut it = token_iter.peekable();
|
||||||
while let Some(token) = it.next() {
|
while let Some(token) = it.next() {
|
||||||
|
let token = token?;
|
||||||
match token.kind() {
|
match token.kind() {
|
||||||
TokenKind::Code => self.write_code(&token),
|
TokenKind::Code => self.write_code(&token)?,
|
||||||
TokenKind::Comment => {}
|
TokenKind::Comment => {}
|
||||||
TokenKind::BufferedCode { escape } => {
|
TokenKind::BufferedCode { escape } => {
|
||||||
self.write_buffered_code(&token, escape)
|
self.write_buffered_code(&token, escape)?
|
||||||
}
|
}
|
||||||
TokenKind::Text => {
|
TokenKind::Text => {
|
||||||
// concatenate repeated text token
|
// concatenate repeated text token
|
||||||
|
@ -143,7 +243,7 @@ impl SourceBuilder {
|
||||||
let mut concatenated = String::new();
|
let mut concatenated = String::new();
|
||||||
concatenated.push_str(token.as_str());
|
concatenated.push_str(token.as_str());
|
||||||
|
|
||||||
while let Some(next_token) = it.peek() {
|
while let Some(&Ok(ref next_token)) = it.peek() {
|
||||||
match next_token.kind() {
|
match next_token.kind() {
|
||||||
TokenKind::Text => {
|
TokenKind::Text => {
|
||||||
concatenated.push_str(next_token.as_str());
|
concatenated.push_str(next_token.as_str());
|
||||||
|
@ -157,10 +257,12 @@ impl SourceBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_token = Token::new(&*concatenated, offset, TokenKind::Text);
|
let new_token = Token::new(&*concatenated, offset, TokenKind::Text);
|
||||||
self.write_text(&new_token);
|
self.write_text(&new_token)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(mut self) -> Result<TranslatedSource, Error> {
|
pub fn finalize(mut self) -> Result<TranslatedSource, Error> {
|
||||||
|
@ -202,6 +304,43 @@ fn into_offset(source: &str, span: Span) -> Option<usize> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TranslatedSource {
|
||||||
|
pub ast: Block,
|
||||||
|
pub source_map: SourceMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
// translate tokens into Rust code
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct Translator {
|
||||||
|
escape: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Translator {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { escape: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn escape(mut self, new: bool) -> Self {
|
||||||
|
self.escape = new;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate<'a>(
|
||||||
|
&self,
|
||||||
|
token_iter: ParseStream<'a>,
|
||||||
|
) -> Result<TranslatedSource, Error> {
|
||||||
|
let original_source = token_iter.original_source;
|
||||||
|
|
||||||
|
let mut ps = SourceBuilder::new(self.escape);
|
||||||
|
ps.reserve(original_source.len());
|
||||||
|
ps.feed_tokens(token_iter)?;
|
||||||
|
|
||||||
|
Ok(ps.finalize()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -209,7 +348,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn translate() {
|
fn translate() {
|
||||||
let src = "<% pub fn sample() { %> <%% <%=//%>\n%><% } %>";
|
let src = "<% pub fn sample() { %> <%% <%=//%>\n1%><% } %>";
|
||||||
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 {
|
||||||
|
@ -217,8 +356,7 @@ mod tests {
|
||||||
source: String::with_capacity(token_iter.original_source.len()),
|
source: String::with_capacity(token_iter.original_source.len()),
|
||||||
source_map: SourceMap::default(),
|
source_map: SourceMap::default(),
|
||||||
};
|
};
|
||||||
ps.feed_tokens(&token_iter.clone().into_vec().unwrap());
|
ps.feed_tokens(token_iter.clone()).unwrap();
|
||||||
eprintln!("{}", ps.source);
|
|
||||||
Translator::new().translate(token_iter).unwrap();
|
Translator::new().translate(token_iter).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "sailfish-macros"
|
name = "sailfish-macros"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
||||||
description = "Really fast, intuitive template engine for Rust"
|
description = "Really fast, intuitive template engine for Rust"
|
||||||
homepage = "https://github.com/Kogia-sima/sailfish"
|
homepage = "https://github.com/Kogia-sima/sailfish"
|
||||||
|
@ -29,6 +29,6 @@ proc-macro2 = "1.0.11"
|
||||||
|
|
||||||
[dependencies.sailfish-compiler]
|
[dependencies.sailfish-compiler]
|
||||||
path = "../sailfish-compiler"
|
path = "../sailfish-compiler"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["procmacro"]
|
features = ["procmacro"]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/out/
|
|
@ -0,0 +1,258 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "afl"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2797f92fb146a37560af914b5d5328f8330d6a39b6eaf00f5b184ac73c0c81e7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"clap",
|
||||||
|
"libc",
|
||||||
|
"rustc_version",
|
||||||
|
"xdg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.33.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags",
|
||||||
|
"strsim",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuzzing-tests"
|
||||||
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"afl",
|
||||||
|
"sailfish",
|
||||||
|
"sailfish-compiler",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoap"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e804a5759b475f44377998918a7e3be9da3767056f5e77751ef7803893db0e9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sailfish"
|
||||||
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"itoap",
|
||||||
|
"ryu",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sailfish-compiler"
|
||||||
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"yaml-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xdg"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaml-rust"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
|
||||||
|
dependencies = [
|
||||||
|
"linked-hash-map",
|
||||||
|
]
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "fuzzing-tests"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
afl = "0.8.0"
|
||||||
|
sailfish = { path = "../../sailfish" }
|
||||||
|
sailfish-compiler = { path = "../../sailfish-compiler" }
|
|
@ -0,0 +1 @@
|
||||||
|
<h1><%= message %></h1>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<table>
|
||||||
|
<% for i in 1..=9 %>
|
||||||
|
<tr>
|
||||||
|
<% for j in 1..=9 %>
|
||||||
|
<td><%= i * j %></td>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<% } %>
|
||||||
|
</table>
|
|
@ -0,0 +1 @@
|
||||||
|
<a href="http://example.com/">
|
|
@ -0,0 +1,5 @@
|
||||||
|
for row in &table {
|
||||||
|
for col in row {
|
||||||
|
println!("{}", *col);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate afl;
|
||||||
|
|
||||||
|
use sailfish_compiler::Compiler;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
fuzz!(|data: &[u8]| {
|
||||||
|
// HTML escaping
|
||||||
|
if let Ok(feed) = std::str::from_utf8(data) {
|
||||||
|
let compiler = Compiler::default();
|
||||||
|
let _ = compiler.compile_str(feed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate afl;
|
||||||
|
|
||||||
|
use sailfish::runtime as sf;
|
||||||
|
use sf::Render;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
fuzz!(|data: &[u8]| {
|
||||||
|
// HTML escaping
|
||||||
|
if let Ok(feed) = std::str::from_utf8(data) {
|
||||||
|
let mut buf = sf::Buffer::new();
|
||||||
|
let _ = feed.render_escaped(&mut buf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
[package]
|
[package]
|
||||||
name = "integration-tests"
|
name = "integration-tests"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Kogia-sima <orcinus4627@gmail.com>"]
|
authors = ["Kogia-sima <orcinus4627@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sailfish = { path = "../sailfish" }
|
sailfish = { path = "../../sailfish" }
|
||||||
sailfish-macros = { path = "../sailfish-macros" }
|
sailfish-macros = { path = "../../sailfish-macros" }
|
||||||
|
sailfish-compiler = { path = "../../sailfish-compiler" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
trybuild = "1.0.28"
|
trybuild = "1.0.28"
|
|
@ -0,0 +1,6 @@
|
||||||
|
template_dir: "../templates"
|
||||||
|
escape: true
|
||||||
|
delimiter: "%"
|
||||||
|
|
||||||
|
optimization:
|
||||||
|
rm_whitespace: false
|
|
@ -0,0 +1,4 @@
|
||||||
|
disp: hello
|
||||||
|
dbg: "hello"
|
||||||
|
disp escaped: hello
|
||||||
|
dbg escaped: "hello"
|
|
@ -0,0 +1,4 @@
|
||||||
|
disp: <%- message | disp %>
|
||||||
|
dbg: <%- message | dbg %>
|
||||||
|
disp escaped: <%= message | disp %>
|
||||||
|
dbg escaped: <%= message | dbg %>
|
|
@ -0,0 +1 @@
|
||||||
|
00010203040506070809
|
|
@ -0,0 +1 @@
|
||||||
|
<% for i in 0..10 { %><%= format!("{:02}", i) %><% } %>
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": <%- name | dbg %>,
|
||||||
|
"value": <%= value %>
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
use sailfish_compiler::Config;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_config() {
|
||||||
|
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("config");
|
||||||
|
let config = Config::search_file_and_read(&*path).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(config.delimiter, '%');
|
||||||
|
assert_eq!(config.escape, true);
|
||||||
|
assert_eq!(config.rm_whitespace, false);
|
||||||
|
assert_eq!(config.template_dirs.len(), 1);
|
||||||
|
}
|
|
@ -191,6 +191,26 @@ fn test_rust_macro() {
|
||||||
assert_render("rust_macro", RustMacro { value: Some(10) });
|
assert_render("rust_macro", RustMacro { value: Some(10) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(TemplateOnce)]
|
||||||
|
#[template(path = "formatting.stpl", escape = false)]
|
||||||
|
struct Formatting;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_formatting() {
|
||||||
|
assert_render("formatting", Formatting);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TemplateOnce)]
|
||||||
|
#[template(path = "filter.stpl")]
|
||||||
|
struct Filter<'a> {
|
||||||
|
message: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filter() {
|
||||||
|
assert_render("filter", Filter { message: "hello" });
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod unix {
|
mod unix {
|
||||||
use super::*;
|
use super::*;
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "sailfish"
|
name = "sailfish"
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
|
||||||
description = "Really fast, intuitive template engine for Rust"
|
description = "Really fast, intuitive template engine for Rust"
|
||||||
homepage = "https://github.com/Kogia-sima/sailfish"
|
homepage = "https://github.com/Kogia-sima/sailfish"
|
||||||
|
|
|
@ -68,15 +68,21 @@ impl Buffer {
|
||||||
self.capacity
|
self.capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Force the length of buffer to `new_len`
|
#[inline]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe fn _set_len(&mut self, new_len: usize) {
|
||||||
|
self.len = new_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Increase the length of buffer by `additional` bytes
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// - `new_len` must be less than or equal to `capacity()`
|
/// - `additional` must be less than or equal to `capacity() - len()`
|
||||||
/// - The elements at `old_len..new_len` must be initialized
|
/// - The elements at `old_len..old_len + additional` must be initialized
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn set_len(&mut self, new_len: usize) {
|
pub unsafe fn advance(&mut self, additional: usize) {
|
||||||
self.len = new_len;
|
self.len += additional;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -86,9 +92,10 @@ impl Buffer {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reserve(&mut self, size: usize) {
|
pub fn reserve(&mut self, size: usize) {
|
||||||
if unlikely!(self.len + size > self.capacity) {
|
if size <= self.capacity.wrapping_sub(self.len) {
|
||||||
self.reserve_internal(size);
|
return;
|
||||||
}
|
}
|
||||||
|
self.reserve_internal(size);
|
||||||
debug_assert!(self.len + size <= self.capacity);
|
debug_assert!(self.len + size <= self.capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +117,9 @@ impl Buffer {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push_str(&mut self, data: &str) {
|
pub fn push_str(&mut self, data: &str) {
|
||||||
let size = data.len();
|
let size = data.len();
|
||||||
self.reserve(size);
|
if unlikely!(size > self.capacity.wrapping_sub(self.len)) {
|
||||||
|
self.reserve_internal(size);
|
||||||
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = self.data.add(self.len);
|
let p = self.data.add(self.len);
|
||||||
std::ptr::copy_nonoverlapping(data.as_ptr(), p, size);
|
std::ptr::copy_nonoverlapping(data.as_ptr(), p, size);
|
||||||
|
@ -129,9 +138,9 @@ impl Buffer {
|
||||||
#[cold]
|
#[cold]
|
||||||
fn reserve_internal(&mut self, size: usize) {
|
fn reserve_internal(&mut self, size: usize) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let new_capacity = std::cmp::max(self.capacity * 2, self.len + size);
|
let new_capacity = std::cmp::max(self.capacity * 2, self.capacity + size);
|
||||||
debug_assert!(new_capacity > self.capacity);
|
debug_assert!(new_capacity > self.capacity);
|
||||||
self.data = safe_realloc(self.data, self.capacity, new_capacity);
|
self.data = safe_realloc(self.data, self.capacity, new_capacity, size);
|
||||||
self.capacity = new_capacity;
|
self.capacity = new_capacity;
|
||||||
}
|
}
|
||||||
debug_assert!(!self.data.is_null());
|
debug_assert!(!self.data.is_null());
|
||||||
|
@ -140,7 +149,13 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
unsafe fn safe_realloc(ptr: *mut u8, capacity: usize, new_capacity: usize) -> *mut u8 {
|
unsafe fn safe_realloc(
|
||||||
|
ptr: *mut u8,
|
||||||
|
capacity: usize,
|
||||||
|
new_capacity: usize,
|
||||||
|
size: usize,
|
||||||
|
) -> *mut u8 {
|
||||||
|
assert!(size <= std::usize::MAX / 2, "capacity is too large");
|
||||||
assert!(new_capacity <= std::usize::MAX / 2, "capacity is too large");
|
assert!(new_capacity <= std::usize::MAX / 2, "capacity is too large");
|
||||||
let data = if unlikely!(capacity == 0) {
|
let data = if unlikely!(capacity == 0) {
|
||||||
let new_layout = Layout::from_size_align_unchecked(new_capacity, 1);
|
let new_layout = Layout::from_size_align_unchecked(new_capacity, 1);
|
||||||
|
@ -303,11 +318,34 @@ mod tests {
|
||||||
assert_eq!(buf.as_str(), "");
|
assert_eq!(buf.as_str(), "");
|
||||||
|
|
||||||
// into empty string
|
// into empty string
|
||||||
let buf = Buffer::new();
|
let buf = Buffer::default();
|
||||||
let mut s = buf.into_string();
|
let mut s = buf.into_string();
|
||||||
assert_eq!(s, "");
|
assert_eq!(s, "");
|
||||||
|
|
||||||
s.push_str("apple");
|
s.push_str("apple");
|
||||||
assert_eq!(s, "apple");
|
assert_eq!(s, "apple");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn clone() {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
let mut s1 = Buffer::with_capacity(0);
|
||||||
|
let mut s2 = s1.clone();
|
||||||
|
|
||||||
|
s1.push('a');
|
||||||
|
s2.push_str("b");
|
||||||
|
|
||||||
|
assert_eq!(s1.as_str(), "a");
|
||||||
|
assert_eq!(s2.as_str(), "b");
|
||||||
|
|
||||||
|
let mut s1 = Buffer::from("foo");
|
||||||
|
let mut s2 = s1.clone();
|
||||||
|
|
||||||
|
s1 = s1 + "bar";
|
||||||
|
write!(s2, "baz").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(s1.as_str(), "foobar");
|
||||||
|
assert_eq!(s2.as_str(), "foobaz");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,15 +40,12 @@ fn contains_key(x: usize) -> bool {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
|
pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
|
||||||
|
debug_assert!(feed.len() >= 16);
|
||||||
|
|
||||||
let len = feed.len();
|
let len = feed.len();
|
||||||
let mut start_ptr = feed.as_ptr();
|
let mut start_ptr = feed.as_ptr();
|
||||||
let end_ptr = start_ptr.add(len);
|
let end_ptr = start_ptr.add(len);
|
||||||
|
|
||||||
if feed.len() < USIZE_BYTES {
|
|
||||||
naive::escape(buffer, start_ptr, start_ptr, end_ptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ptr = start_ptr;
|
let mut ptr = start_ptr;
|
||||||
let aligned_ptr = ptr.add(USIZE_BYTES - (start_ptr as usize & USIZE_ALIGN));
|
let aligned_ptr = ptr.add(USIZE_BYTES - (start_ptr as usize & USIZE_ALIGN));
|
||||||
debug_assert_eq!(aligned_ptr as usize % USIZE_BYTES, 0);
|
debug_assert_eq!(aligned_ptr as usize % USIZE_BYTES, 0);
|
||||||
|
|
|
@ -50,10 +50,6 @@ fn escape(feed: &str, buf: &mut Buffer) {
|
||||||
unsafe { fun(feed, buf) };
|
unsafe { fun(feed, buf) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the default escape function
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn register_escape_fn(_fun: fn(&str, &mut Buffer)) {}
|
|
||||||
|
|
||||||
/// write the escaped contents into `Buffer`
|
/// write the escaped contents into `Buffer`
|
||||||
#[cfg_attr(feature = "perf-inline", inline)]
|
#[cfg_attr(feature = "perf-inline", inline)]
|
||||||
pub fn escape_to_buf(feed: &str, buf: &mut Buffer) {
|
pub fn escape_to_buf(feed: &str, buf: &mut Buffer) {
|
||||||
|
@ -61,7 +57,7 @@ pub fn escape_to_buf(feed: &str, buf: &mut Buffer) {
|
||||||
if feed.len() < 16 {
|
if feed.len() < 16 {
|
||||||
buf.reserve(feed.len() * 6);
|
buf.reserve(feed.len() * 6);
|
||||||
let l = naive::escape_small(feed, buf.as_mut_ptr().add(buf.len()));
|
let l = naive::escape_small(feed, buf.as_mut_ptr().add(buf.len()));
|
||||||
buf.set_len(buf.len() + l);
|
buf.advance(l);
|
||||||
} else {
|
} else {
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
{
|
{
|
||||||
|
@ -119,6 +115,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn noescape() {
|
fn noescape() {
|
||||||
assert_eq!(escape(""), "");
|
assert_eq!(escape(""), "");
|
||||||
|
assert_eq!(escape("1234567890"), "1234567890");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
escape("abcdefghijklmnopqrstrvwxyz"),
|
escape("abcdefghijklmnopqrstrvwxyz"),
|
||||||
"abcdefghijklmnopqrstrvwxyz"
|
"abcdefghijklmnopqrstrvwxyz"
|
||||||
|
@ -159,12 +156,12 @@ mod tests {
|
||||||
const ASCII_CHARS: &'static [u8] = br##"abcdefghijklmnopqrstuvwxyz0123456789-^\@[;:],./\!"#$%&'()~=~|`{+*}<>?_"##;
|
const ASCII_CHARS: &'static [u8] = br##"abcdefghijklmnopqrstuvwxyz0123456789-^\@[;:],./\!"#$%&'()~=~|`{+*}<>?_"##;
|
||||||
let mut state = 88172645463325252u64;
|
let mut state = 88172645463325252u64;
|
||||||
let mut data = Vec::with_capacity(100);
|
let mut data = Vec::with_capacity(100);
|
||||||
let mut buf1 = Buffer::new();
|
|
||||||
let mut buf2 = Buffer::new();
|
|
||||||
let mut buf3 = Buffer::new();
|
|
||||||
|
|
||||||
for len in 0..100 {
|
let mut buf_naive = Buffer::new();
|
||||||
for _ in 0..10 {
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
|
for len in 16..100 {
|
||||||
|
for _ in 0..5 {
|
||||||
data.clear();
|
data.clear();
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
// xorshift
|
// xorshift
|
||||||
|
@ -178,23 +175,33 @@ mod tests {
|
||||||
|
|
||||||
let s = unsafe { std::str::from_utf8_unchecked(&*data) };
|
let s = unsafe { std::str::from_utf8_unchecked(&*data) };
|
||||||
|
|
||||||
buf1.clear();
|
|
||||||
buf2.clear();
|
|
||||||
buf3.clear();
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
escape_to_buf(s, &mut buf1);
|
|
||||||
fallback::escape(s, &mut buf2);
|
|
||||||
naive::escape(
|
naive::escape(
|
||||||
&mut buf3,
|
&mut buf_naive,
|
||||||
s.as_ptr(),
|
s.as_ptr(),
|
||||||
s.as_ptr(),
|
s.as_ptr(),
|
||||||
s.as_ptr().add(s.len()),
|
s.as_ptr().add(s.len()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
dbg!(s);
|
||||||
|
fallback::escape(s, &mut buf);
|
||||||
|
assert_eq!(buf.as_str(), buf_naive.as_str());
|
||||||
|
buf.clear();
|
||||||
|
|
||||||
|
if is_x86_feature_detected!("sse2") {
|
||||||
|
sse2::escape(s, &mut buf);
|
||||||
|
assert_eq!(buf.as_str(), buf_naive.as_str());
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_x86_feature_detected!("avx2") {
|
||||||
|
avx2::escape(s, &mut buf);
|
||||||
|
assert_eq!(buf.as_str(), buf_naive.as_str());
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(buf1.as_str(), buf3.as_str());
|
buf_naive.clear();
|
||||||
assert_eq!(buf2.as_str(), buf3.as_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
// TODO: performance improvement
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::{Buffer, Render, RenderError};
|
||||||
|
|
||||||
|
pub struct Display<'a, T>(&'a T);
|
||||||
|
|
||||||
|
impl<'a, T: fmt::Display> Render for Display<'a, T> {
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
use fmt::Write;
|
||||||
|
|
||||||
|
write!(b, "{}", self.0).map_err(|e| RenderError::from(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// render using `std::fmt::Display` trait
|
||||||
|
#[inline]
|
||||||
|
pub fn disp<T: fmt::Display>(expr: &T) -> Display<T> {
|
||||||
|
Display(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Debug<'a, T>(&'a T);
|
||||||
|
|
||||||
|
impl<'a, T: fmt::Debug> Render for Debug<'a, T> {
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
use fmt::Write;
|
||||||
|
|
||||||
|
write!(b, "{:?}", self.0).map_err(|e| RenderError::from(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// render using `std::fmt::Debug` trait
|
||||||
|
#[inline]
|
||||||
|
pub fn dbg<T: fmt::Debug>(expr: &T) -> Debug<T> {
|
||||||
|
Debug(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Upper<'a, T>(&'a T);
|
||||||
|
|
||||||
|
impl<'a, T: Render> Render for Upper<'a, T> {
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
let old_len = b.len();
|
||||||
|
self.0.render(b)?;
|
||||||
|
|
||||||
|
let s = b.as_str()[old_len..].to_uppercase();
|
||||||
|
unsafe { b._set_len(old_len) };
|
||||||
|
b.push_str(&*s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// convert the rendered contents to uppercase
|
||||||
|
#[inline]
|
||||||
|
pub fn upper<T: Render>(expr: &T) -> Upper<T> {
|
||||||
|
Upper(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lower<'a, T>(&'a T);
|
||||||
|
|
||||||
|
impl<'a, T: Render> Render for Lower<'a, T> {
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
let old_len = b.len();
|
||||||
|
self.0.render(b)?;
|
||||||
|
|
||||||
|
let s = b.as_str()[old_len..].to_lowercase();
|
||||||
|
unsafe { b._set_len(old_len) };
|
||||||
|
b.push_str(&*s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
let old_len = b.len();
|
||||||
|
self.0.render_escaped(b)?;
|
||||||
|
|
||||||
|
let s = b.as_str()[old_len..].to_lowercase();
|
||||||
|
unsafe { b._set_len(old_len) };
|
||||||
|
b.push_str(&*s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// convert the rendered contents to lowercase
|
||||||
|
#[inline]
|
||||||
|
pub fn lower<T: Render>(expr: &T) -> Lower<T> {
|
||||||
|
Lower(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn case() {
|
||||||
|
let mut buf = Buffer::new();
|
||||||
|
upper(&"hElLO, WOrLd!").render(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.as_str(), "HELLO, WORLD!");
|
||||||
|
|
||||||
|
buf.clear();
|
||||||
|
lower(&"hElLO, WOrLd!").render(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.as_str(), "hello, world!");
|
||||||
|
|
||||||
|
buf.clear();
|
||||||
|
lower(&"<h1>TITLE</h1>").render_escaped(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.as_str(), "<h1>title</h1>");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,29 +1,29 @@
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! render {
|
macro_rules! render {
|
||||||
($ctx:ident, $value:expr) => {
|
($buf:ident, $value:expr) => {
|
||||||
(&($value))._sf_r_internal(&mut $ctx.buf)?
|
$crate::runtime::Render::render(&($value), &mut $buf)?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! render_escaped {
|
macro_rules! render_escaped {
|
||||||
($ctx:ident, $value:expr) => {
|
($buf:ident, $value:expr) => {
|
||||||
(&($value))._sf_re_internal(&mut $ctx.buf)?
|
$crate::runtime::Render::render_escaped(&($value), &mut $buf)?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! render_text {
|
macro_rules! render_text {
|
||||||
($ctx:ident, $value:expr) => {
|
($buf:ident, $value:expr) => {
|
||||||
$ctx.buf.push_str($value)
|
$buf.push_str($value)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! render_noop {
|
macro_rules! render_noop {
|
||||||
($ctx:ident, $value:expr) => {};
|
($buf:ident, $value:expr) => {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ mod utils;
|
||||||
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
pub mod escape;
|
pub mod escape;
|
||||||
|
pub mod filter;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod render;
|
mod render;
|
||||||
mod size_hint;
|
mod size_hint;
|
||||||
|
@ -69,19 +70,6 @@ impl From<fmt::Error> for RenderError {
|
||||||
|
|
||||||
pub type RenderResult = Result<String, RenderError>;
|
pub type RenderResult = Result<String, RenderError>;
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct Context {
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub buf: Buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
#[inline]
|
|
||||||
pub fn into_result(self) -> RenderResult {
|
|
||||||
Ok(self.buf.into_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[inline(never)]
|
// #[inline(never)]
|
||||||
// pub fn _instantiate(table: Vec<Vec<usize>>) -> String {
|
// pub fn _instantiate(table: Vec<Vec<usize>>) -> String {
|
||||||
// let mut buffer = Buffer::with_capacity(130000);
|
// let mut buffer = Buffer::with_capacity(130000);
|
||||||
|
@ -92,9 +80,25 @@ impl Context {
|
||||||
// let _ = (&r2).render(&mut buffer);
|
// let _ = (&r2).render(&mut buffer);
|
||||||
// buffer.push_str("</td><td>");
|
// buffer.push_str("</td><td>");
|
||||||
// }
|
// }
|
||||||
// unsafe { buffer.set_len(buffer.len() - 4) }
|
// unsafe { buffer._set_len(buffer.len() - 4) }
|
||||||
// buffer.push_str("</tr>");
|
// buffer.push_str("</tr>");
|
||||||
// }
|
// }
|
||||||
// buffer.push_str("</table>");
|
// buffer.push_str("</table>");
|
||||||
// buffer.into_string()
|
// buffer.into_string()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn render_error() {
|
||||||
|
let err = RenderError::new("custom error");
|
||||||
|
assert!(err.source().is_none());
|
||||||
|
assert_eq!(format!("{}", err), "custom error");
|
||||||
|
|
||||||
|
let err = RenderError::from(std::fmt::Error::default());
|
||||||
|
assert!(err.source().is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
use std::path::Path;
|
use std::borrow::Cow;
|
||||||
|
use std::cell::{Ref, RefMut};
|
||||||
|
use std::num::{
|
||||||
|
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize,
|
||||||
|
NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
|
||||||
|
};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::{Arc, MutexGuard, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
use super::buffer::Buffer;
|
use super::buffer::Buffer;
|
||||||
use super::{escape, RenderError};
|
use super::{escape, RenderError};
|
||||||
|
@ -59,16 +67,30 @@ pub trait Render {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl Render for str {
|
impl Render for String {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
b.push_str(self);
|
b.push_str(&**self);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
escape::escape_to_buf(self, b);
|
escape::escape_to_buf(&**self, b);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for &str {
|
||||||
|
#[inline]
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
b.push_str(*self);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
escape::escape_to_buf(*self, b);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +116,21 @@ impl Render for char {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Render for PathBuf {
|
||||||
|
#[inline]
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
// TODO: speed up on Windows using OsStrExt
|
||||||
|
b.push_str(&*self.to_string_lossy());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
escape::escape_to_buf(&*self.to_string_lossy(), b);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Render for Path {
|
impl Render for Path {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
@ -160,7 +197,7 @@ macro_rules! render_int {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = b.as_mut_ptr().add(b.len());
|
let ptr = b.as_mut_ptr().add(b.len());
|
||||||
let l = itoap::write_to_ptr(ptr, *self);
|
let l = itoap::write_to_ptr(ptr, *self);
|
||||||
b.set_len(b.len() + l);
|
b.advance(l);
|
||||||
}
|
}
|
||||||
debug_assert!(b.len() <= b.capacity());
|
debug_assert!(b.len() <= b.capacity());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -186,7 +223,7 @@ impl Render for f32 {
|
||||||
b.reserve(16);
|
b.reserve(16);
|
||||||
let ptr = b.as_mut_ptr().add(b.len());
|
let ptr = b.as_mut_ptr().add(b.len());
|
||||||
let l = ryu::raw::format32(*self, ptr);
|
let l = ryu::raw::format32(*self, ptr);
|
||||||
b.set_len(b.len() + l);
|
b.advance(l);
|
||||||
debug_assert!(b.len() <= b.capacity());
|
debug_assert!(b.len() <= b.capacity());
|
||||||
}
|
}
|
||||||
} else if self.is_nan() {
|
} else if self.is_nan() {
|
||||||
|
@ -215,7 +252,7 @@ impl Render for f64 {
|
||||||
b.reserve(24);
|
b.reserve(24);
|
||||||
let ptr = b.as_mut_ptr().add(b.len());
|
let ptr = b.as_mut_ptr().add(b.len());
|
||||||
let l = ryu::raw::format64(*self, ptr);
|
let l = ryu::raw::format64(*self, ptr);
|
||||||
b.set_len(b.len() + l);
|
b.advance(l);
|
||||||
debug_assert!(b.len() <= b.capacity());
|
debug_assert!(b.len() <= b.capacity());
|
||||||
}
|
}
|
||||||
} else if self.is_nan() {
|
} else if self.is_nan() {
|
||||||
|
@ -236,22 +273,80 @@ impl Render for f64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private trait for avoiding method name collision in render* macros
|
macro_rules! render_deref {
|
||||||
#[doc(hidden)]
|
(
|
||||||
pub trait RenderInternal {
|
$(#[doc = $doc:tt])*
|
||||||
fn _sf_r_internal(&self, b: &mut Buffer) -> Result<(), RenderError>;
|
[$($bounds:tt)+] $($desc:tt)+
|
||||||
fn _sf_re_internal(&self, b: &mut Buffer) -> Result<(), RenderError>;
|
) => {
|
||||||
|
$(#[doc = $doc])*
|
||||||
|
impl <$($bounds)+> Render for $($desc)+ {
|
||||||
|
#[inline]
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
(**self).render(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
(**self).render_escaped(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Render + ?Sized> RenderInternal for T {
|
render_deref!(['a, T: Render + ?Sized] &'a T);
|
||||||
|
render_deref!(['a, T: Render + ?Sized] &'a mut T);
|
||||||
|
render_deref!([T: Render + ?Sized] Box<T>);
|
||||||
|
render_deref!([T: Render + ?Sized] Rc<T>);
|
||||||
|
render_deref!([T: Render + ?Sized] Arc<T>);
|
||||||
|
render_deref!(['a, T: Render + ToOwned + ?Sized] Cow<'a, T>);
|
||||||
|
render_deref!(['a, T: Render + ?Sized] Ref<'a, T>);
|
||||||
|
render_deref!(['a, T: Render + ?Sized] RefMut<'a, T>);
|
||||||
|
render_deref!(['a, T: Render + ?Sized] MutexGuard<'a, T>);
|
||||||
|
render_deref!(['a, T: Render + ?Sized] RwLockReadGuard<'a, T>);
|
||||||
|
render_deref!(['a, T: Render + ?Sized] RwLockWriteGuard<'a, T>);
|
||||||
|
|
||||||
|
macro_rules! render_nonzero {
|
||||||
|
($($type:ty,)*) => {
|
||||||
|
$(
|
||||||
|
impl Render for $type {
|
||||||
|
#[inline]
|
||||||
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
self.get().render(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
|
self.get().render_escaped(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render_nonzero!(
|
||||||
|
NonZeroI8,
|
||||||
|
NonZeroI16,
|
||||||
|
NonZeroI32,
|
||||||
|
NonZeroI64,
|
||||||
|
NonZeroI128,
|
||||||
|
NonZeroIsize,
|
||||||
|
NonZeroU8,
|
||||||
|
NonZeroU16,
|
||||||
|
NonZeroU32,
|
||||||
|
NonZeroU64,
|
||||||
|
NonZeroU128,
|
||||||
|
NonZeroUsize,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<T: Render> Render for Wrapping<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn _sf_r_internal(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
self.render(b)
|
self.0.render(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn _sf_re_internal(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
|
||||||
self.render_escaped(b)
|
self.0.render_escaped(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,34 +357,33 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn receiver_coercion() {
|
fn receiver_coercion() {
|
||||||
let mut b = Buffer::new();
|
let mut b = Buffer::new();
|
||||||
(&1)._sf_r_internal(&mut b).unwrap();
|
Render::render(&1, &mut b).unwrap();
|
||||||
(&&1)._sf_r_internal(&mut b).unwrap();
|
Render::render(&&1, &mut b).unwrap();
|
||||||
(&&&1)._sf_r_internal(&mut b).unwrap();
|
Render::render(&&&1, &mut b).unwrap();
|
||||||
(&&&&1)._sf_r_internal(&mut b).unwrap();
|
Render::render(&&&&1, &mut b).unwrap();
|
||||||
assert_eq!(b.as_str(), "1111");
|
assert_eq!(b.as_str(), "1111");
|
||||||
b.clear();
|
b.clear();
|
||||||
|
|
||||||
let v = 2.0;
|
Render::render(&true, &mut b).unwrap();
|
||||||
(&v)._sf_r_internal(&mut b).unwrap();
|
Render::render(&&false, &mut b).unwrap();
|
||||||
(&&v)._sf_r_internal(&mut b).unwrap();
|
Render::render(&&&true, &mut b).unwrap();
|
||||||
(&&&v)._sf_r_internal(&mut b).unwrap();
|
Render::render(&&&&false, &mut b).unwrap();
|
||||||
(&&&&v)._sf_r_internal(&mut b).unwrap();
|
assert_eq!(b.as_str(), "truefalsetruefalse");
|
||||||
assert_eq!(b.as_str(), "2.02.02.02.0");
|
|
||||||
b.clear();
|
b.clear();
|
||||||
|
|
||||||
let s = "apple";
|
let s = "apple";
|
||||||
(&*s)._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&s, &mut b).unwrap();
|
||||||
(&s)._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&s, &mut b).unwrap();
|
||||||
(&&s)._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&&s, &mut b).unwrap();
|
||||||
(&&&s)._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&&&s, &mut b).unwrap();
|
||||||
(&&&&s)._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&&&&s, &mut b).unwrap();
|
||||||
assert_eq!(b.as_str(), "appleappleappleappleapple");
|
assert_eq!(b.as_str(), "appleappleappleappleapple");
|
||||||
b.clear();
|
b.clear();
|
||||||
|
|
||||||
(&'c')._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&'c', &mut b).unwrap();
|
||||||
(&&'<')._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&&'<', &mut b).unwrap();
|
||||||
(&&&'&')._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&&&'&', &mut b).unwrap();
|
||||||
(&&&&' ')._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&&&&' ', &mut b).unwrap();
|
||||||
assert_eq!(b.as_str(), "c<& ");
|
assert_eq!(b.as_str(), "c<& ");
|
||||||
b.clear();
|
b.clear();
|
||||||
}
|
}
|
||||||
|
@ -300,11 +394,29 @@ mod tests {
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
let mut b = Buffer::new();
|
let mut b = Buffer::new();
|
||||||
(&String::from("a"))._sf_r_internal(&mut b).unwrap();
|
Render::render(&String::from("a"), &mut b).unwrap();
|
||||||
(&&PathBuf::from("b"))._sf_r_internal(&mut b).unwrap();
|
Render::render(&&PathBuf::from("b"), &mut b).unwrap();
|
||||||
(&Rc::new(4u32))._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&Rc::new(4u32), &mut b).unwrap();
|
||||||
(&Rc::new(2.3f32))._sf_re_internal(&mut b).unwrap();
|
Render::render_escaped(&Rc::new(2.3f32), &mut b).unwrap();
|
||||||
|
|
||||||
assert_eq!(b.as_str(), "ab42.3");
|
assert_eq!(b.as_str(), "ab42.3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float() {
|
||||||
|
let mut b = Buffer::new();
|
||||||
|
|
||||||
|
Render::render_escaped(&0.0f64, &mut b).unwrap();
|
||||||
|
Render::render_escaped(&std::f64::INFINITY, &mut b).unwrap();
|
||||||
|
Render::render_escaped(&std::f64::NEG_INFINITY, &mut b).unwrap();
|
||||||
|
Render::render_escaped(&std::f64::NAN, &mut b).unwrap();
|
||||||
|
assert_eq!(b.as_str(), "0.0inf-infNaN");
|
||||||
|
b.clear();
|
||||||
|
|
||||||
|
Render::render_escaped(&0.0f32, &mut b).unwrap();
|
||||||
|
Render::render_escaped(&std::f32::INFINITY, &mut b).unwrap();
|
||||||
|
Render::render_escaped(&std::f32::NEG_INFINITY, &mut b).unwrap();
|
||||||
|
Render::render_escaped(&std::f32::NAN, &mut b).unwrap();
|
||||||
|
assert_eq!(b.as_str(), "0.0inf-infNaN");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue