diff --git a/Cargo.lock b/Cargo.lock index 926713f..c9addeb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,7 @@ dependencies = [ "parser", "proc-macro2", "quote", + "rewriter", "serde", "syn", ] @@ -177,6 +178,13 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rewriter" +version = "0.1.0" +dependencies = [ + "nom", +] + [[package]] name = "serde" version = "1.0.195" diff --git a/Cargo.toml b/Cargo.toml index b21a729..619d99e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["derive", "lib", "parser"] +members = ["derive", "lib", "parser", "rewriter"] resolver = "2" diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 19c02dc..1b61f66 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -26,6 +26,7 @@ with-warp = [] [dependencies] parser = { path = "../parser" } +rewriter = { path = "../rewriter" } mime = "0.3" mime_guess = "2" proc-macro2 = "1" diff --git a/derive/src/config.rs b/derive/src/config.rs index 8f37ae9..4641d76 100644 --- a/derive/src/config.rs +++ b/derive/src/config.rs @@ -280,7 +280,7 @@ where } #[allow(clippy::match_wild_err_arm)] -pub(crate) fn get_source(tpl_path: &Path) -> std::result::Result { +pub(crate) fn get_template_source(tpl_path: &Path) -> std::result::Result { match fs::read_to_string(tpl_path) { Err(_) => Err(format!( "unable to open template file '{}'", @@ -291,15 +291,11 @@ pub(crate) fn get_source(tpl_path: &Path) -> std::result::Result std::result::Result { - get_source(tpl_path).and_then(|source| crate::rewriter::rewrite_source(tpl_path, source)) -} - static CONFIG_FILE_NAME: &str = "askama.toml"; static DEFAULT_SYNTAX_NAME: &str = "default"; static DEFAULT_ESCAPERS: &[(&[&str], &str)] = &[ @@ -320,7 +316,8 @@ mod tests { let path = Config::new("", None) .and_then(|config| config.find_template("b.html", None)) .unwrap(); - assert_eq!(super::get_source(&path).unwrap(), "bar"); + #[rustfmt::skip] + assert_eq!(get_template_source(&path).unwrap(), "{% macro b() %}\nbar{% endmacro b %}\n"); } #[test] diff --git a/derive/src/input.rs b/derive/src/input.rs index 70b64eb..d5f993c 100644 --- a/derive/src/input.rs +++ b/derive/src/input.rs @@ -219,7 +219,7 @@ impl TemplateArgs { if args.source.is_some() { return Err("must specify 'source' or 'path', not both".into()); } - args.source = Some(Source::Source(crate::rewriter::transform_path(s.value()))); + args.source = Some(Source::Source(rewriter::transform_path(s.value()))); args.ext = Some("html".into()); } else { return Err("template path must be string literal".into()); diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 98c8b2a..e48ba35 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -18,9 +18,6 @@ use heritage::{Context, Heritage}; mod input; use input::{Print, TemplateArgs, TemplateInput}; -mod node; -mod rewriter; - #[proc_macro_derive(Template, attributes(template))] pub fn derive_template(input: TokenStream) -> TokenStream { let ast = syn::parse::(input).unwrap(); diff --git a/lib/tests/template.rs b/lib/tests/template.rs index cfb4b5c..9388759 100644 --- a/lib/tests/template.rs +++ b/lib/tests/template.rs @@ -1,4 +1,3 @@ -/* use jrsx::Template; #[derive(Template)] @@ -30,4 +29,3 @@ fn test_template2() {
\nSuper!\n
\n" ); } -*/ diff --git a/rewriter/Cargo.toml b/rewriter/Cargo.toml new file mode 100644 index 0000000..c0fcd2a --- /dev/null +++ b/rewriter/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rewriter" +version = "0.1.0" +edition = "2021" + +[lib] +doctest = false + +[dependencies] +nom = { version = "7", default-features = false, features = ["alloc"] } diff --git a/rewriter/src/lib.rs b/rewriter/src/lib.rs new file mode 100644 index 0000000..4ca8f6b --- /dev/null +++ b/rewriter/src/lib.rs @@ -0,0 +1,55 @@ +#![deny(elided_lifetimes_in_paths)] +#![deny(unreachable_pub)] + +mod parser; +mod rewriter; + +use parser::Ast; +use rewriter::normalize; +use rewriter::Rewriter; +use std::path::Path; + +pub fn transform_path>(path: P) -> String { + let macro_name = normalize(&path); + let macro_path = path.as_ref().display(); + + format!( + "\ + {{%- import \"{macro_path}\" as {macro_name}_scope -%}}\n\ + {{% call {macro_name}_scope::{macro_name}() %}}{{% endcall %}}\n" + ) +} + +pub fn rewrite_source>(path: P, source: String) -> String { + let macro_name = normalize(path); + + let parsed = match Ast::from_str(&source) { + Ok(parsed) => parsed, + Err(_) => return source, + }; + + Rewriter::new(&parsed.nodes) + .build(¯o_name) + .unwrap_or(source) +} + +#[test] +fn test_transform_path() { + assert_eq!( + transform_path("templates/hello_world.html"), + "\ + {%- import \"templates/hello_world.html\" as hello_world_scope -%}\n\ + {% call hello_world_scope::hello_world() %}{% endcall %}\n" + ); +} + +#[test] +fn test_rewrite_source() { + assert_eq!( + rewrite_source("index", "".into()), + "\ + {%- import \"hello.html\" as hello_scope -%}\n\ + {% macro index() %}\n\ + {% call hello_scope::hello(name) %}{% endcall %}{% endmacro index %}\n" + ); +} diff --git a/derive/src/node.rs b/rewriter/src/parser.rs similarity index 93% rename from derive/src/node.rs rename to rewriter/src/parser.rs index a989cc4..29ebd65 100644 --- a/derive/src/node.rs +++ b/rewriter/src/parser.rs @@ -20,7 +20,6 @@ use nom::multi::many0; use nom::sequence::delimited; use nom::sequence::terminated; use nom::sequence::tuple; -use parser::ParseError; const JSX_BLOCK_START: &str = "<"; const JSX_BLOCK_END: &str = ">"; @@ -30,37 +29,20 @@ const MACRO_DEF_END: &str = "#}"; type ParseResult<'a, T = &'a str> = nom::IResult<&'a str, T>; -pub(crate) struct Parsed { - pub(crate) ast: Ast<'static>, - #[allow(dead_code)] - pub(crate) source: String, -} - -impl Parsed { - pub(crate) fn new(source: String) -> Result { - let src = unsafe { std::mem::transmute::<&str, &'static str>(source.as_str()) }; - let ast = Ast::from_str(src)?; - - Ok(Self { ast, source }) - } - - pub(crate) fn nodes(&self) -> &[Node<'_>] { - &self.ast.nodes - } -} +pub(crate) struct ParseError; #[derive(Debug)] pub(crate) struct Ast<'a> { - nodes: Vec>, + pub(crate) nodes: Vec>, } impl<'a> Ast<'a> { - fn from_str(src: &'a str) -> Result { + pub(crate) fn from_str(src: &'a str) -> Result { let parse = |i: &'a str| Node::many(i); match terminated(parse, cut(eof))(src) { Ok(("", nodes)) => Ok(Self { nodes }), - err => panic!("{:#?}", err), + Ok(_) | Err(_) => Err(ParseError), } } } diff --git a/derive/src/rewriter.rs b/rewriter/src/rewriter.rs similarity index 65% rename from derive/src/rewriter.rs rename to rewriter/src/rewriter.rs index 828a330..d4c722d 100644 --- a/derive/src/rewriter.rs +++ b/rewriter/src/rewriter.rs @@ -1,50 +1,23 @@ -use crate::generator::Buffer; -use crate::node::JsxBlock; -use crate::node::JsxClose; -use crate::node::MacroDef; -use crate::node::Node; -use crate::node::Parsed; -use crate::CompileError; +use crate::parser::JsxBlock; +use crate::parser::JsxClose; +use crate::parser::MacroDef; +use crate::parser::Node; use std::collections::HashSet; use std::path::Path; -pub(crate) fn transform_path

(path: P) -> String -where - P: AsRef, -{ - let macro_name = normalize(&path); - let macro_path = path.as_ref().display(); - - format!( - "\ - {{%- import \"{macro_path}\" as {macro_name}_scope -%}}\n\ - {{% call {macro_name}_scope::{macro_name}() %}}{{% endcall %}}\n" - ) -} - -pub(crate) fn rewrite_source

(path: P, source: String) -> Result -where - P: AsRef, -{ - let macro_name = normalize(path); - - let parsed = Parsed::new(source)?; - let source = Rewriter::new(parsed.nodes()).build(¯o_name)?; - - Ok(source) -} +pub(crate) struct CompileError; -struct Rewriter<'a> { +pub(crate) struct Rewriter<'a> { nodes: &'a [Node<'a>], } impl<'a> Rewriter<'a> { - fn new(nodes: &'a [Node<'a>]) -> Self { + pub(crate) fn new(nodes: &'a [Node<'a>]) -> Self { Self { nodes } } - fn build(&self, macro_name: &str) -> Result { - let mut buf = Buffer::new(0); + pub(crate) fn build(&self, macro_name: &str) -> Result { + let mut buf = Buffer::new(); self.rewrite_template(&mut buf, macro_name)?; @@ -154,7 +127,29 @@ impl<'a> Rewriter<'a> { } } -fn normalize

(path: P) -> String +pub(crate) struct Buffer { + pub(crate) buf: String, +} + +impl Buffer { + pub(crate) fn new() -> Self { + Self { buf: String::new() } + } + + pub(crate) fn writeln(&mut self, s: &str) -> Result<(), CompileError> { + if !s.is_empty() { + self.write(s); + } + self.buf.push('\n'); + Ok(()) + } + + pub(crate) fn write(&mut self, s: &str) { + self.buf.push_str(s); + } +} + +pub(crate) fn normalize

(path: P) -> String where P: AsRef, { @@ -166,31 +161,3 @@ where .to_lowercase() .replace(['-', '.'], "_") } - -#[test] -fn test_transform_path() { - assert_eq!( - transform_path("templates/hello_world.html"), - "\ - {%- import \"templates/hello_world.html\" as hello_world_scope -%}\n\ - {% call hello_world_scope::hello_world() %}{% endcall %}\n" - ); -} - -#[test] -fn test_rewrite_source() { - assert_eq!( - rewrite_source("index", "".into()).unwrap(), - "\ - {%- import \"hello.html\" as hello_scope -%}\n\ - {% macro index() %}\n\ - {% call hello_scope::hello(name) %}{% endcall %}{% endmacro index %}\n" - ); -} - -#[test] -fn test_normalize() { - assert_eq!(normalize("templates/hello_world.html"), "hello_world"); - assert_eq!(normalize("templates/hello-world.html"), "hello_world"); - assert_eq!(normalize("templates/hello.world.html"), "hello_world"); -}