Skip to content

Commit

Permalink
feat: split rewriter (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
cvng authored Feb 3, 2024
1 parent 9854b18 commit 7765344
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 101 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
members = ["derive", "lib", "parser"]
members = ["derive", "lib", "parser", "rewriter"]
resolver = "2"
1 change: 1 addition & 0 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ with-warp = []

[dependencies]
parser = { path = "../parser" }
rewriter = { path = "../rewriter" }
mime = "0.3"
mime_guess = "2"
proc-macro2 = "1"
Expand Down
11 changes: 4 additions & 7 deletions derive/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ where
}

#[allow(clippy::match_wild_err_arm)]
pub(crate) fn get_source(tpl_path: &Path) -> std::result::Result<String, CompileError> {
pub(crate) fn get_template_source(tpl_path: &Path) -> std::result::Result<String, CompileError> {
match fs::read_to_string(tpl_path) {
Err(_) => Err(format!(
"unable to open template file '{}'",
Expand All @@ -291,15 +291,11 @@ pub(crate) fn get_source(tpl_path: &Path) -> std::result::Result<String, Compile
if source.ends_with('\n') {
let _ = source.pop();
}
Ok(source)
Ok(rewriter::rewrite_source(tpl_path, source))
}
}
}

pub(crate) fn get_template_source(tpl_path: &Path) -> std::result::Result<String, CompileError> {
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)] = &[
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion derive/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
3 changes: 0 additions & 3 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<syn::DeriveInput>(input).unwrap();
Expand Down
2 changes: 0 additions & 2 deletions lib/tests/template.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/*
use jrsx::Template;

#[derive(Template)]
Expand Down Expand Up @@ -30,4 +29,3 @@ fn test_template2() {
<div>\nSuper!\n</div>\n"
);
}
*/
10 changes: 10 additions & 0 deletions rewriter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"] }
55 changes: 55 additions & 0 deletions rewriter/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<P: AsRef<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<P: AsRef<Path>>(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(&macro_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", "<Hello name />".into()),
"\
{%- import \"hello.html\" as hello_scope -%}\n\
{% macro index() %}\n\
{% call hello_scope::hello(name) %}{% endcall %}{% endmacro index %}\n"
);
}
26 changes: 4 additions & 22 deletions derive/src/node.rs → rewriter/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ">";
Expand All @@ -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<Self, ParseError> {
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<Node<'a>>,
pub(crate) nodes: Vec<Node<'a>>,
}

impl<'a> Ast<'a> {
fn from_str(src: &'a str) -> Result<Self, ParseError> {
pub(crate) fn from_str(src: &'a str) -> Result<Self, ParseError> {
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),
}
}
}
Expand Down
97 changes: 32 additions & 65 deletions derive/src/rewriter.rs → rewriter/src/rewriter.rs
Original file line number Diff line number Diff line change
@@ -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<P>(path: P) -> String
where
P: AsRef<Path>,
{
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<P>(path: P, source: String) -> Result<String, CompileError>
where
P: AsRef<Path>,
{
let macro_name = normalize(path);

let parsed = Parsed::new(source)?;
let source = Rewriter::new(parsed.nodes()).build(&macro_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<String, CompileError> {
let mut buf = Buffer::new(0);
pub(crate) fn build(&self, macro_name: &str) -> Result<String, CompileError> {
let mut buf = Buffer::new();

self.rewrite_template(&mut buf, macro_name)?;

Expand Down Expand Up @@ -154,7 +127,29 @@ impl<'a> Rewriter<'a> {
}
}

fn normalize<P>(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<P>(path: P) -> String
where
P: AsRef<Path>,
{
Expand All @@ -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", "<Hello name />".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");
}

0 comments on commit 7765344

Please sign in to comment.