diff --git a/benchmarks/benches/askama.rs b/benchmarks/benches/askama.rs index b2c9dae..0a12ecc 100644 --- a/benchmarks/benches/askama.rs +++ b/benchmarks/benches/askama.rs @@ -5,7 +5,8 @@ extern crate test; use askama::Template; #[derive(Template)] -#[template(source = r#" +#[template( + source = r#" <html> <head> <title>{{year}}</title> @@ -20,29 +21,48 @@ use askama::Template; {% endfor %} </ul> </body> -</html>"#, ext="html")] +</html>"#, + ext = "html" +)] struct BenchTemplate { year: &'static str, - teams: Vec<Entry> + teams: Vec<Entry>, } struct Entry { name: &'static str, - score: u16 + score: u16, } #[bench] fn render_template(b: &mut test::Bencher) { - let teams = vec![Entry {name: "Jiangsu", score: 43}, - Entry {name: "Beijing", score: 27}, - Entry {name: "Guangzhou", score: 22}, - Entry {name: "Shandong", score: 12}]; - let hello = test::black_box(BenchTemplate{ year: "2015", teams }); + let teams = vec![ + Entry { + name: "Jiangsu", + score: 43, + }, + Entry { + name: "Beijing", + score: 27, + }, + Entry { + name: "Guangzhou", + score: 22, + }, + Entry { + name: "Shandong", + score: 12, + }, + ]; + let hello = test::black_box(BenchTemplate { + year: "2015", + teams, + }); b.iter(|| { // Instead of simply call hello.render().unwrap(), rendering to // a string with a good capacity gives a ~10% speed increase here - let mut s = String::with_capacity(500); - hello.render_into(&mut s).unwrap(); + let mut s = String::with_capacity(500); + hello.render_into(&mut s).unwrap(); }); } diff --git a/benchmarks/benches/complicated_maud.rs b/benchmarks/benches/complicated_maud.rs index 8d0a02f..2e27ef0 100644 --- a/benchmarks/benches/complicated_maud.rs +++ b/benchmarks/benches/complicated_maud.rs @@ -3,7 +3,7 @@ extern crate test; -use maud::{Markup, html}; +use maud::{html, Markup}; #[derive(Debug)] struct Entry { @@ -12,12 +12,12 @@ struct Entry { } mod btn { - use maud::{Markup, Render, html}; + use maud::{html, Markup, Render}; #[derive(Copy, Clone)] pub enum RequestMethod { Get, - Post + Post, } #[derive(Copy, Clone)] @@ -77,24 +77,39 @@ fn layout<S: AsRef<str>>(title: S, inner: Markup) -> Markup { fn render_complicated_template(b: &mut test::Bencher) { let year = test::black_box("2015"); let teams = test::black_box(vec![ - Entry { name: "Jiangsu", score: 43 }, - Entry { name: "Beijing", score: 27 }, - Entry { name: "Guangzhou", score: 22 }, - Entry { name: "Shandong", score: 12 }, + Entry { + name: "Jiangsu", + score: 43, + }, + Entry { + name: "Beijing", + score: 27, + }, + Entry { + name: "Guangzhou", + score: 22, + }, + Entry { + name: "Shandong", + score: 12, + }, ]); b.iter(|| { use crate::btn::{Button, RequestMethod}; - layout(format!("Homepage of {}", year), html! { - h1 { "Hello there!" } + layout( + format!("Homepage of {}", year), + html! { + h1 { "Hello there!" } - @for entry in &teams { - div { - strong { (entry.name) } - (Button::new("Edit", "edit")) - (Button::new("Delete", "edit") - .with_method(RequestMethod::Post)) + @for entry in &teams { + div { + strong { (entry.name) } + (Button::new("Edit", "edit")) + (Button::new("Delete", "edit") + .with_method(RequestMethod::Post)) + } } - } - }) + }, + ) }); } diff --git a/benchmarks/benches/handlebars.rs b/benchmarks/benches/handlebars.rs index 8181ea6..01e8f44 100644 --- a/benchmarks/benches/handlebars.rs +++ b/benchmarks/benches/handlebars.rs @@ -4,8 +4,8 @@ extern crate test; -use serde_json::value::{Map, Value as Json}; use handlebars::{to_json, Handlebars}; +use serde_json::value::{Map, Value as Json}; static SOURCE: &'static str = "<html> <head> @@ -30,14 +30,12 @@ fn make_data() -> Map<String, Json> { let mut teams = Vec::new(); - for &(name, score) in - &[ - ("Jiangsu", 43u16), - ("Beijing", 27u16), - ("Guangzhou", 22u16), - ("Shandong", 12u16), - ] - { + for &(name, score) in &[ + ("Jiangsu", 43u16), + ("Beijing", 27u16), + ("Guangzhou", 22u16), + ("Shandong", 12u16), + ] { let mut t = Map::new(); t.insert("name".to_string(), to_json(&name)); t.insert("score".to_string(), to_json(&score)); @@ -51,7 +49,8 @@ fn make_data() -> Map<String, Json> { #[bench] fn render_template(b: &mut test::Bencher) { let mut handlebars = Handlebars::new(); - handlebars.register_template_string("table", SOURCE.to_string()) + handlebars + .register_template_string("table", SOURCE.to_string()) .expect("Invalid template format"); let data = make_data(); diff --git a/benchmarks/benches/horrorshow.rs b/benchmarks/benches/horrorshow.rs index 76fbe56..5e483a1 100644 --- a/benchmarks/benches/horrorshow.rs +++ b/benchmarks/benches/horrorshow.rs @@ -15,10 +15,22 @@ struct Entry { fn render_template(b: &mut test::Bencher) { let year = test::black_box("2015"); let teams = test::black_box(vec![ - Entry { name: "Jiangsu", score: 43 }, - Entry { name: "Beijing", score: 27 }, - Entry { name: "Guangzhou", score: 22 }, - Entry { name: "Shandong", score: 12 }, + Entry { + name: "Jiangsu", + score: 43, + }, + Entry { + name: "Beijing", + score: 27, + }, + Entry { + name: "Guangzhou", + score: 22, + }, + Entry { + name: "Shandong", + score: 12, + }, ]); b.iter(|| { (html! { @@ -40,6 +52,8 @@ fn render_template(b: &mut test::Bencher) { } } } - }).into_string().unwrap() + }) + .into_string() + .unwrap() }); } diff --git a/benchmarks/benches/liquid.rs b/benchmarks/benches/liquid.rs index e2baa0a..9db5515 100644 --- a/benchmarks/benches/liquid.rs +++ b/benchmarks/benches/liquid.rs @@ -2,8 +2,8 @@ extern crate test; -use liquid::ParserBuilder; use liquid::model::{Object, Value}; +use liquid::ParserBuilder; static SOURCE: &'static str = "<html> <head> @@ -30,7 +30,13 @@ fn make_team(name: &'static str, score: u16) -> Value { #[bench] fn render_template(b: &mut test::Bencher) { - let template = test::black_box(ParserBuilder::with_stdlib().build().unwrap().parse(SOURCE).unwrap()); + let template = test::black_box( + ParserBuilder::with_stdlib() + .build() + .unwrap() + .parse(SOURCE) + .unwrap(), + ); let mut globals = test::black_box({ let mut globals = Object::new(); globals.insert("year".into(), Value::scalar(2015)); diff --git a/benchmarks/benches/maud.rs b/benchmarks/benches/maud.rs index dd54324..adaf4ed 100644 --- a/benchmarks/benches/maud.rs +++ b/benchmarks/benches/maud.rs @@ -15,10 +15,22 @@ struct Entry { fn render_template(b: &mut test::Bencher) { let year = test::black_box("2015"); let teams = test::black_box(vec![ - Entry { name: "Jiangsu", score: 43 }, - Entry { name: "Beijing", score: 27 }, - Entry { name: "Guangzhou", score: 22 }, - Entry { name: "Shandong", score: 12 }, + Entry { + name: "Jiangsu", + score: 43, + }, + Entry { + name: "Beijing", + score: 27, + }, + Entry { + name: "Guangzhou", + score: 22, + }, + Entry { + name: "Shandong", + score: 12, + }, ]); b.iter(|| { html! { diff --git a/benchmarks/benches/tera.rs b/benchmarks/benches/tera.rs index 3299315..6197f4c 100644 --- a/benchmarks/benches/tera.rs +++ b/benchmarks/benches/tera.rs @@ -34,12 +34,27 @@ fn render_template(b: &mut test::Bencher) { let context = test::black_box({ let mut context = Context::new(); - context.insert("teams", &[ - Entry { name: "Jiangsu", score: 43 }, - Entry { name: "Beijing", score: 27 }, - Entry { name: "Guangzhou", score: 22 }, - Entry { name: "Shandong", score: 12 }, - ]); + context.insert( + "teams", + &[ + Entry { + name: "Jiangsu", + score: 43, + }, + Entry { + name: "Beijing", + score: 27, + }, + Entry { + name: "Guangzhou", + score: 22, + }, + Entry { + name: "Shandong", + score: 12, + }, + ], + ); context.insert("year", &"2015"); context }); diff --git a/docs/src/main.rs b/docs/src/main.rs index 6909131..19d8121 100644 --- a/docs/src/main.rs +++ b/docs/src/main.rs @@ -1,18 +1,18 @@ #![feature(proc_macro_hygiene)] -use comrak::{self, Arena, ComrakOptions}; use comrak::nodes::{AstNode, NodeCodeBlock, NodeHeading, NodeHtmlBlock, NodeLink, NodeValue}; +use comrak::{self, Arena, ComrakOptions}; use serde_json; -use std::error::Error; use std::env; +use std::error::Error; use std::fs::{self, File}; use std::io::{self, BufReader}; use std::mem; use std::path::Path; use std::string::FromUtf8Error; -use syntect::parsing::SyntaxSet; use syntect::highlighting::{Color, ThemeSet}; use syntect::html::highlighted_html_for_string; +use syntect::parsing::SyntaxSet; use crate::page::Page; use crate::string_writer::StringWriter; @@ -24,12 +24,15 @@ mod views; fn main() -> Result<(), Box<dyn Error>> { let args = env::args().collect::<Vec<_>>(); if args.len() >= 3 && &args[1] == "build-nav" && args[3..].iter().all(|arg| arg.contains(":")) { - let entries = args[3..].iter().map(|arg| { - let mut splits = arg.splitn(2, ":"); - let slug = splits.next().unwrap(); - let input_path = splits.next().unwrap(); - (slug, input_path) - }).collect::<Vec<_>>(); + let entries = args[3..] + .iter() + .map(|arg| { + let mut splits = arg.splitn(2, ":"); + let slug = splits.next().unwrap(); + let input_path = splits.next().unwrap(); + (slug, input_path) + }) + .collect::<Vec<_>>(); build_nav(&entries, &args[2]) } else if args.len() == 6 && &args[1] == "build-page" { build_page(&args[2], &args[3], &args[4], &args[5]) @@ -42,10 +45,13 @@ fn build_nav(entries: &[(&str, &str)], nav_path: &str) -> Result<(), Box<dyn Err let arena = Arena::new(); let options = comrak_options(); - let nav = entries.iter().map(|&(slug, input_path)| { - let title = load_page_title(&arena, &options, input_path)?; - Ok((slug, title)) - }).collect::<io::Result<Vec<_>>>()?; + let nav = entries + .iter() + .map(|&(slug, input_path)| { + let title = load_page_title(&arena, &options, input_path)?; + Ok((slug, title)) + }) + .collect::<io::Result<Vec<_>>>()?; // Only write if different to avoid spurious rebuilds let old_string = fs::read_to_string(nav_path).unwrap_or(String::new()); @@ -73,12 +79,15 @@ fn build_page( let arena = Arena::new(); let options = comrak_options(); - let nav = nav.iter().filter_map(|(slug, title)| { - title.as_ref().map(|title| { - let title = comrak::parse_document(&arena, title, &options); - (slug.as_str(), title) + let nav = nav + .iter() + .filter_map(|(slug, title)| { + title.as_ref().map(|title| { + let title = comrak::parse_document(&arena, title, &options); + (slug.as_str(), title) + }) }) - }).collect::<Vec<_>>(); + .collect::<Vec<_>>(); let page = load_page(&arena, &options, input_path)?; let markup = views::main(&options, slug, page, &nav); @@ -99,8 +108,7 @@ fn load_page<'a>( lower_headings(page.content); rewrite_md_links(page.content) .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - highlight_code(page.content) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + highlight_code(page.content).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; Ok(page) } @@ -113,11 +121,7 @@ fn load_page_title<'a>( let page = load_page_raw(arena, options, path)?; let title = page.title.map(|title| { let mut buffer = String::new(); - comrak::format_commonmark( - title, - options, - &mut StringWriter(&mut buffer), - ).unwrap(); + comrak::format_commonmark(title, options, &mut StringWriter(&mut buffer)).unwrap(); buffer }); Ok(title) @@ -131,18 +135,16 @@ fn load_page_raw<'a>( let buffer = fs::read_to_string(path)?; let content = comrak::parse_document(arena, &buffer, options); - let title = content - .first_child() - .filter(|node| { - let mut data = node.data.borrow_mut(); - if let NodeValue::Heading(NodeHeading { level: 1, .. }) = data.value { - node.detach(); - data.value = NodeValue::Document; - true - } else { - false - } - }); + let title = content.first_child().filter(|node| { + let mut data = node.data.borrow_mut(); + if let NodeValue::Heading(NodeHeading { level: 1, .. }) = data.value { + node.detach(); + data.value = NodeValue::Document; + true + } else { + false + } + }); Ok(Page { title, content }) } @@ -175,12 +177,18 @@ fn highlight_code<'a>(root: &'a AstNode<'a>) -> Result<(), FromUtf8Error> { let ss = SyntaxSet::load_defaults_newlines(); let ts = ThemeSet::load_defaults(); let mut theme = ts.themes["InspiredGitHub"].clone(); - theme.settings.background = Some(Color { r: 0xff, g: 0xee, b: 0xff, a: 0xff }); + theme.settings.background = Some(Color { + r: 0xff, + g: 0xee, + b: 0xff, + a: 0xff, + }); for node in root.descendants() { let mut data = node.data.borrow_mut(); - if let NodeValue::CodeBlock(NodeCodeBlock { info, literal, .. }) = &mut data.value { + if let NodeValue::CodeBlock(NodeCodeBlock { info, literal, .. }) = &mut data.value { let info = String::from_utf8(mem::replace(info, Vec::new()))?; - let syntax = ss.find_syntax_by_token(&info) + let syntax = ss + .find_syntax_by_token(&info) .unwrap_or_else(|| ss.find_syntax_plain_text()); let mut literal = String::from_utf8(mem::replace(literal, Vec::new()))?; if !literal.ends_with('\n') { diff --git a/docs/src/page.rs b/docs/src/page.rs index 7f5a6c5..42e9bc0 100644 --- a/docs/src/page.rs +++ b/docs/src/page.rs @@ -4,4 +4,3 @@ pub struct Page<'a> { pub title: Option<&'a AstNode<'a>>, pub content: &'a AstNode<'a>, } - diff --git a/docs/src/views.rs b/docs/src/views.rs index 06e0c84..43cae7f 100644 --- a/docs/src/views.rs +++ b/docs/src/views.rs @@ -1,8 +1,8 @@ -use comrak::{self, ComrakOptions}; -use comrak::nodes::AstNode; -use crate::Page; use crate::string_writer::StringWriter; -use maud::{DOCTYPE, Markup, PreEscaped, Render, html}; +use crate::Page; +use comrak::nodes::AstNode; +use comrak::{self, ComrakOptions}; +use maud::{html, Markup, PreEscaped, Render, DOCTYPE}; use std::str; struct Comrak<'a>(&'a AstNode<'a>, &'a ComrakOptions); @@ -22,7 +22,12 @@ impl<'a> Render for ComrakRemovePTags<'a> { let mut buffer = String::new(); comrak::format_html(self.0, self.1, &mut StringWriter(&mut buffer)).unwrap(); assert!(buffer.starts_with("<p>") && buffer.ends_with("</p>\n")); - PreEscaped(buffer.trim_start_matches("<p>").trim_end_matches("</p>\n").to_string()) + PreEscaped( + buffer + .trim_start_matches("<p>") + .trim_end_matches("</p>\n") + .to_string(), + ) } } diff --git a/maud/src/lib.rs b/maud/src/lib.rs index 02e1386..8d6b153 100644 --- a/maud/src/lib.rs +++ b/maud/src/lib.rs @@ -9,9 +9,12 @@ #![doc(html_root_url = "https://docs.rs/maud/0.22.0")] -#[cfg(feature = "actix-web")] extern crate actix_web_dep; -#[cfg(feature = "iron")] extern crate iron; -#[cfg(feature = "rocket")] extern crate rocket; +#[cfg(feature = "actix-web")] +extern crate actix_web_dep; +#[cfg(feature = "iron")] +extern crate iron; +#[cfg(feature = "rocket")] +extern crate rocket; use std::fmt::{self, Write}; @@ -161,12 +164,12 @@ pub const DOCTYPE: PreEscaped<&'static str> = PreEscaped("<!DOCTYPE html>"); #[cfg(feature = "iron")] mod iron_support { - use std::io; + use crate::PreEscaped; use iron::headers::ContentType; use iron::modifier::{Modifier, Set}; use iron::modifiers::Header; use iron::response::{Response, WriteBody}; - use crate::PreEscaped; + use std::io; impl Modifier<Response> for PreEscaped<String> { fn modify(self, response: &mut Response) { @@ -185,11 +188,11 @@ mod iron_support { #[cfg(feature = "rocket")] mod rocket_support { + use crate::PreEscaped; use rocket::http::{ContentType, Status}; use rocket::request::Request; use rocket::response::{Responder, Response}; use std::io::Cursor; - use crate::PreEscaped; impl Responder<'static> for PreEscaped<String> { fn respond_to(self, _: &Request) -> Result<Response<'static>, Status> { @@ -204,7 +207,7 @@ mod rocket_support { #[cfg(feature = "actix-web")] mod actix_support { use crate::PreEscaped; - use actix_web_dep::{Responder, HttpResponse, HttpRequest, Error}; + use actix_web_dep::{Error, HttpRequest, HttpResponse, Responder}; use futures::future::{ok, Ready}; impl Responder for PreEscaped<String> { @@ -212,8 +215,8 @@ mod actix_support { type Future = Ready<Result<HttpResponse, Self::Error>>; fn respond_to(self, _req: &HttpRequest) -> Self::Future { ok(HttpResponse::Ok() - .content_type("text/html; charset=utf-8") - .body(self.0)) + .content_type("text/html; charset=utf-8") + .body(self.0)) } } } diff --git a/maud/tests/absolute_paths.rs b/maud/tests/absolute_paths.rs index baee0b3..690af2e 100644 --- a/maud/tests/absolute_paths.rs +++ b/maud/tests/absolute_paths.rs @@ -1,5 +1,4 @@ #![feature(proc_macro_hygiene)] - // Make sure `std` is available but the prelude isn't #![no_std] extern crate std; diff --git a/maud/tests/basic_syntax.rs b/maud/tests/basic_syntax.rs index 42aafb9..94dc510 100644 --- a/maud/tests/basic_syntax.rs +++ b/maud/tests/basic_syntax.rs @@ -1,6 +1,6 @@ #![feature(proc_macro_hygiene)] -use maud::{Markup, html}; +use maud::{html, Markup}; #[test] fn literals() { @@ -22,7 +22,8 @@ fn semicolons() { "three"; ;;;;;;;;;;;;;;;;;;;;;;;; "four"; - }.into_string(); + } + .into_string(); assert_eq!(s, "onetwothreefour"); } @@ -34,7 +35,8 @@ fn blocks() { " ducks" " geese" } " swans" - }.into_string(); + } + .into_string(); assert_eq!(s, "hello ducks geese swans"); } @@ -63,10 +65,15 @@ fn simple_attributes() { section id="midriff" { p class="hotpink" { "Hello!" } } - }.into_string(); - assert_eq!(s, concat!( + } + .into_string(); + assert_eq!( + s, + concat!( r#"<link rel="stylesheet" href="styles.css">"#, - r#"<section id="midriff"><p class="hotpink">Hello!</p></section>"#)); + r#"<section id="midriff"><p class="hotpink">Hello!</p></section>"# + ) + ); } #[test] @@ -83,17 +90,24 @@ fn toggle_empty_attributes() { input checked?[false]; input checked?[rocks]; input checked?[!rocks]; - }).into_string(); - assert_eq!(s, concat!( + }) + .into_string(); + assert_eq!( + s, + concat!( r#"<input checked>"#, r#"<input>"#, r#"<input checked>"#, - r#"<input>"#)); + r#"<input>"# + ) + ); } #[test] fn toggle_empty_attributes_braces() { - struct Maud { rocks: bool } + struct Maud { + rocks: bool, + } let s = html!(input checked?[Maud { rocks: true }.rocks] /).into_string(); assert_eq!(s, r#"<input checked>"#); } @@ -101,10 +115,14 @@ fn toggle_empty_attributes_braces() { #[test] fn colons_in_names() { let s = html!(pon-pon:controls-alpha { a on:click="yay()" { "Yay!" } }).into_string(); - assert_eq!(s, concat!( + assert_eq!( + s, + concat!( r#"<pon-pon:controls-alpha>"#, r#"<a on:click="yay()">Yay!</a>"#, - r#"</pon-pon:controls-alpha>"#)); + r#"</pon-pon:controls-alpha>"# + ) + ); } #[rustfmt::skip::macros(html)] @@ -155,15 +173,29 @@ fn toggle_classes() { fn test(is_cupcake: bool, is_muffin: bool) -> Markup { html!(p.cupcake[is_cupcake].muffin[is_muffin] { "Testing!" }) } - assert_eq!(test(true, true).into_string(), r#"<p class="cupcake muffin">Testing!</p>"#); - assert_eq!(test(false, true).into_string(), r#"<p class=" muffin">Testing!</p>"#); - assert_eq!(test(true, false).into_string(), r#"<p class="cupcake">Testing!</p>"#); - assert_eq!(test(false, false).into_string(), r#"<p class="">Testing!</p>"#); + assert_eq!( + test(true, true).into_string(), + r#"<p class="cupcake muffin">Testing!</p>"# + ); + assert_eq!( + test(false, true).into_string(), + r#"<p class=" muffin">Testing!</p>"# + ); + assert_eq!( + test(true, false).into_string(), + r#"<p class="cupcake">Testing!</p>"# + ); + assert_eq!( + test(false, false).into_string(), + r#"<p class="">Testing!</p>"# + ); } #[test] fn toggle_classes_braces() { - struct Maud { rocks: bool } + struct Maud { + rocks: bool, + } let s = html!(p.rocks[Maud { rocks: true }.rocks] { "Awesome!" }).into_string(); assert_eq!(s, r#"<p class="rocks">Awesome!</p>"#); } @@ -181,8 +213,14 @@ fn mixed_classes() { fn test(is_muffin: bool) -> Markup { html!(p.cupcake.muffin[is_muffin].lamington { "Testing!" }) } - assert_eq!(test(true).into_string(), r#"<p class="cupcake lamington muffin">Testing!</p>"#); - assert_eq!(test(false).into_string(), r#"<p class="cupcake lamington">Testing!</p>"#); + assert_eq!( + test(true).into_string(), + r#"<p class="cupcake lamington muffin">Testing!</p>"# + ); + assert_eq!( + test(false).into_string(), + r#"<p class="cupcake lamington">Testing!</p>"# + ); } #[test] @@ -200,7 +238,10 @@ fn id_string() { #[test] fn classes_attrs_ids_mixed_up() { let s = html!(p { "Hi, " span.name.here lang="en" #thing { "Lyra" } "!" }).into_string(); - assert_eq!(s, r#"<p>Hi, <span class="name here" id="thing" lang="en">Lyra</span>!</p>"#); + assert_eq!( + s, + r#"<p>Hi, <span class="name here" id="thing" lang="en">Lyra</span>!</p>"# + ); } #[test] @@ -218,11 +259,17 @@ fn div_shorthand_id() { #[test] fn div_shorthand_class_with_attrs() { let s = html!(.awesome-class contenteditable? dir="rtl" #unique-id {}).into_string(); - assert_eq!(s, r#"<div class="awesome-class" id="unique-id" contenteditable dir="rtl"></div>"#); + assert_eq!( + s, + r#"<div class="awesome-class" id="unique-id" contenteditable dir="rtl"></div>"# + ); } #[test] fn div_shorthand_id_with_attrs() { let s = html!(#unique-id contenteditable? dir="rtl" .awesome-class {}).into_string(); - assert_eq!(s, r#"<div class="awesome-class" id="unique-id" contenteditable dir="rtl"></div>"#); + assert_eq!( + s, + r#"<div class="awesome-class" id="unique-id" contenteditable dir="rtl"></div>"# + ); } diff --git a/maud/tests/control_structures.rs b/maud/tests/control_structures.rs index b4b07ba..18cf2fa 100644 --- a/maud/tests/control_structures.rs +++ b/maud/tests/control_structures.rs @@ -15,7 +15,8 @@ fn if_expr() { } @else { "oh noes" } - }.into_string(); + } + .into_string(); assert_eq!(s, name); } } @@ -25,13 +26,13 @@ fn if_expr_in_class() { for &(chocolate_milk, expected) in &[ (0, r#"<p class="empty">Chocolate milk</p>"#), (1, r#"<p class="full">Chocolate milk</p>"#), - ] - { + ] { let s = html! { p.@if chocolate_milk == 0 { "empty" } @else { "full" } { "Chocolate milk" } - }.into_string(); + } + .into_string(); assert_eq!(s, expected); } } @@ -45,7 +46,8 @@ fn if_let() { } @else { "oh noes" } - }.into_string(); + } + .into_string(); assert_eq!(s, output); } } @@ -59,7 +61,8 @@ fn while_expr() { li { (numbers.next().unwrap()) } } } - }.into_string(); + } + .into_string(); assert_eq!(s, "<ul><li>0</li><li>1</li><li>2</li></ul>"); } @@ -72,7 +75,8 @@ fn while_let_expr() { li { (n) } } } - }.into_string(); + } + .into_string(); assert_eq!(s, "<ul><li>0</li><li>1</li><li>2</li></ul>"); } @@ -85,13 +89,18 @@ fn for_expr() { li { (pony) } } } - }.into_string(); - assert_eq!(s, concat!( + } + .into_string(); + assert_eq!( + s, + concat!( "<ul>", "<li>Apple Bloom</li>", "<li>Scootaloo</li>", "<li>Sweetie Belle</li>", - "</ul>")); + "</ul>" + ) + ); } #[test] @@ -106,7 +115,8 @@ fn match_expr() { "oh noes" }, } - }.into_string(); + } + .into_string(); assert_eq!(s, output); } } @@ -119,7 +129,8 @@ fn match_expr_without_delims() { Some(value) => (value), None => span { "oh noes" }, } - }.into_string(); + } + .into_string(); assert_eq!(s, output); } } @@ -132,7 +143,8 @@ fn match_no_trailing_comma() { Some(value) => { (value) } None => span { "oh noes" } } - }.into_string(); + } + .into_string(); assert_eq!(s, output); } } @@ -146,21 +158,27 @@ fn match_expr_with_guards() { Some(value) => (value), None => "none", } - }.into_string(); + } + .into_string(); assert_eq!(s, output); } } #[test] fn match_in_attribute() { - for &(input, output) in &[(1, "<span class=\"one\">1</span>"), (2, "<span class=\"two\">2</span>"), (3, "<span class=\"many\">3</span>")] { + for &(input, output) in &[ + (1, "<span class=\"one\">1</span>"), + (2, "<span class=\"two\">2</span>"), + (3, "<span class=\"many\">3</span>"), + ] { let s = html! { span class=@match input { 1 => "one", 2 => "two", _ => "many", } { (input) } - }.into_string(); + } + .into_string(); assert_eq!(s, output); } } @@ -170,7 +188,8 @@ fn let_expr() { let s = html! { @let x = 42; "I have " (x) " cupcakes!" - }.into_string(); + } + .into_string(); assert_eq!(s, "I have 42 cupcakes!"); } @@ -183,10 +202,12 @@ fn let_lexical_scope() { "Twilight thought I had " (x) " cupcakes, " } "but I only had " (x) "." - }.into_string(); - assert_eq!(s, concat!( - "Twilight thought I had 99 cupcakes, ", - "but I only had 42.")); + } + .into_string(); + assert_eq!( + s, + concat!("Twilight thought I had 99 cupcakes, ", "but I only had 42.") + ); } #[test] @@ -194,6 +215,7 @@ fn let_type_ascription() { let s = html! { @let mut x: Box<dyn Iterator<Item=u32>> = Box::new(vec![42].into_iter()); "I have " (x.next().unwrap()) " cupcakes!" - }.into_string(); + } + .into_string(); assert_eq!(s, "I have 42 cupcakes!"); } diff --git a/maud/tests/splices.rs b/maud/tests/splices.rs index 2b59334..9d256a2 100644 --- a/maud/tests/splices.rs +++ b/maud/tests/splices.rs @@ -25,7 +25,8 @@ fn blocks() { } result }) - }).into_string(); + }) + .into_string(); assert_eq!(s, "3628800"); } @@ -95,7 +96,8 @@ fn structs() { }; let s = html!({ "Name: " (pinkie.name) ". Rating: " (pinkie.repugnance()) - }).into_string(); + }) + .into_string(); assert_eq!(s, "Name: Pinkie Pie. Rating: 1"); } diff --git a/maud_htmlescape/lib.rs b/maud_htmlescape/lib.rs index 5ab8535..c682c06 100644 --- a/maud_htmlescape/lib.rs +++ b/maud_htmlescape/lib.rs @@ -56,8 +56,8 @@ impl<'a> fmt::Write for Escaper<'a> { #[cfg(test)] mod test { - use std::fmt::Write; use crate::Escaper; + use std::fmt::Write; #[test] fn it_works() { diff --git a/maud_macros/src/ast.rs b/maud_macros/src/ast.rs index c437a1d..473f8b9 100644 --- a/maud_macros/src/ast.rs +++ b/maud_macros/src/ast.rs @@ -42,19 +42,20 @@ impl Markup { Markup::Literal { span, .. } => span, Markup::Symbol { ref symbol } => span_tokens(symbol.clone()), Markup::Splice { outer_span, .. } => outer_span, - Markup::Element { ref name, ref body, .. } => { + Markup::Element { + ref name, ref body, .. + } => { let name_span = span_tokens(name.clone()); name_span.join_range(body.span()) - }, - Markup::Let { at_span, ref tokens } => { - at_span.join_range(span_tokens(tokens.clone())) - }, - Markup::Special { ref segments } => { - join_ranges(segments.iter().map(Special::span)) - }, - Markup::Match { at_span, arms_span, .. } => { - at_span.join_range(arms_span) - }, + } + Markup::Let { + at_span, + ref tokens, + } => at_span.join_range(span_tokens(tokens.clone())), + Markup::Special { ref segments } => join_ranges(segments.iter().map(Special::span)), + Markup::Match { + at_span, arms_span, .. + } => at_span.join_range(arms_span), } } } @@ -80,7 +81,11 @@ pub enum Attr { impl Attr { pub fn span(&self) -> SpanRange { match *self { - Attr::Class { dot_span, ref name, ref toggler } => { + Attr::Class { + dot_span, + ref name, + ref toggler, + } => { let name_span = name.span(); let dot_name_span = dot_span.join_range(name_span); if let Some(toggler) = toggler { @@ -88,11 +93,14 @@ impl Attr { } else { dot_name_span } - }, - Attr::Id { hash_span, ref name } => { + } + Attr::Id { + hash_span, + ref name, + } => { let name_span = name.span(); hash_span.join_range(name_span) - }, + } Attr::Attribute { ref attribute } => attribute.span(), } } @@ -158,12 +166,8 @@ impl Attribute { #[derive(Debug)] pub enum AttrType { - Normal { - value: Markup, - }, - Empty { - toggler: Option<Toggler>, - }, + Normal { value: Markup }, + Empty { toggler: Option<Toggler> }, } impl AttrType { @@ -193,11 +197,11 @@ pub struct MatchArm { pub body: Block, } -pub fn span_tokens<I: IntoIterator<Item=TokenTree>>(tokens: I) -> SpanRange { +pub fn span_tokens<I: IntoIterator<Item = TokenTree>>(tokens: I) -> SpanRange { join_ranges(tokens.into_iter().map(|s| SpanRange::single_span(s.span()))) } -pub fn join_ranges<I: IntoIterator<Item=SpanRange>>(ranges: I) -> SpanRange { +pub fn join_ranges<I: IntoIterator<Item = SpanRange>>(ranges: I) -> SpanRange { let mut iter = ranges.into_iter(); let first = match iter.next() { Some(span) => span, diff --git a/maud_macros/src/generate.rs b/maud_macros/src/generate.rs index 357896e..976ad98 100644 --- a/maud_macros/src/generate.rs +++ b/maud_macros/src/generate.rs @@ -1,15 +1,7 @@ use maud_htmlescape::Escaper; -use proc_macro2::{ - Delimiter, - Group, - Literal, - Span, - Ident, - TokenStream, - TokenTree, -}; -use quote::quote; +use proc_macro2::{Delimiter, Group, Ident, Literal, Span, TokenStream, TokenTree}; use proc_macro_error::SpanRange; +use quote::quote; use crate::ast::*; @@ -40,13 +32,22 @@ impl Generator { fn markup(&self, markup: Markup, build: &mut Builder) { match markup { - Markup::Block(Block { markups, outer_span }) => { - if markups.iter().any(|markup| matches!(*markup, Markup::Let { .. })) { - build.push_tokens(self.block(Block { markups, outer_span })); + Markup::Block(Block { + markups, + outer_span, + }) => { + if markups + .iter() + .any(|markup| matches!(*markup, Markup::Let { .. })) + { + build.push_tokens(self.block(Block { + markups, + outer_span, + })); } else { self.markups(markups, build); } - }, + } Markup::Literal { content, .. } => build.push_escaped(&content), Markup::Symbol { symbol } => self.name(symbol, build), Markup::Splice { expr, .. } => build.push_tokens(self.splice(expr)), @@ -56,22 +57,30 @@ impl Generator { for segment in segments { build.push_tokens(self.special(segment)); } - }, - Markup::Match { head, arms, arms_span, .. } => { + } + Markup::Match { + head, + arms, + arms_span, + .. + } => { build.push_tokens({ - let body = arms - .into_iter() - .map(|arm| self.match_arm(arm)) - .collect(); + let body = arms.into_iter().map(|arm| self.match_arm(arm)).collect(); let mut body = TokenTree::Group(Group::new(Delimiter::Brace, body)); body.set_span(arms_span.collapse()); quote!(#head #body) }); - }, + } } } - fn block(&self, Block { markups, outer_span }: Block) -> TokenStream { + fn block( + &self, + Block { + markups, + outer_span, + }: Block, + ) -> TokenStream { let mut build = self.builder(); self.markups(markups, &mut build); let mut block = TokenTree::Group(Group::new(Delimiter::Brace, build.finish())); @@ -93,13 +102,7 @@ impl Generator { }) } - fn element( - &self, - name: TokenStream, - attrs: Attrs, - body: ElementBody, - build: &mut Builder, - ) { + fn element(&self, name: TokenStream, attrs: Attrs, body: ElementBody, build: &mut Builder) { build.push_str("<"); self.name(name.clone(), build); self.attrs(attrs, build); @@ -113,7 +116,10 @@ impl Generator { } fn name(&self, name: TokenStream, build: &mut Builder) { - let string = name.into_iter().map(|token| token.to_string()).collect::<String>(); + let string = name + .into_iter() + .map(|token| token.to_string()) + .collect::<String>(); build.push_escaped(&string); } @@ -126,12 +132,14 @@ impl Generator { build.push_str("=\""); self.markup(value, build); build.push_str("\""); - }, + } AttrType::Empty { toggler: None } => { build.push_str(" "); self.name(name, build); - }, - AttrType::Empty { toggler: Some(toggler) } => { + } + AttrType::Empty { + toggler: Some(toggler), + } => { let head = desugar_toggler(toggler); build.push_tokens({ let mut build = self.builder(); @@ -140,7 +148,7 @@ impl Generator { let body = build.finish(); quote!(#head { #body }) }) - }, + } } } } @@ -171,7 +179,7 @@ fn desugar_attrs(attrs: Attrs) -> Vec<Attribute> { } else { classes_static.push(name); } - }, + } Attr::Id { name, .. } => ids.push(name), Attr::Attribute { attribute } => attributes.push(attribute), } @@ -201,7 +209,11 @@ fn desugar_classes_or_ids( }; let head = desugar_toggler(toggler); markups.push(Markup::Special { - segments: vec![Special { at_span: SpanRange::call_site(), head, body }], + segments: vec![Special { + at_span: SpanRange::call_site(), + head, + body, + }], }); } Some(Attribute { @@ -228,7 +240,12 @@ fn prepend_leading_space(name: Markup, leading_space: &mut bool) -> Vec<Markup> markups } -fn desugar_toggler(Toggler { mut cond, cond_span }: Toggler) -> TokenStream { +fn desugar_toggler( + Toggler { + mut cond, + cond_span, + }: Toggler, +) -> TokenStream { // If the expression contains an opening brace `{`, // wrap it in parentheses to avoid parse errors if cond.clone().into_iter().any(is_braced_block) { diff --git a/maud_macros/src/lib.rs b/maud_macros/src/lib.rs index dd63725..a142516 100644 --- a/maud_macros/src/lib.rs +++ b/maud_macros/src/lib.rs @@ -1,7 +1,5 @@ #![feature(proc_macro_hygiene)] - #![doc(html_root_url = "https://docs.rs/maud_macros/0.22.0")] - // TokenStream values are reference counted, and the mental overhead of tracking // lifetimes outweighs the marginal gains from explicit borrowing #![allow(clippy::needless_pass_by_value)] @@ -13,8 +11,8 @@ mod generate; mod parse; use proc_macro2::{Ident, TokenStream, TokenTree}; +use proc_macro_error::proc_macro_error; use quote::quote; -use proc_macro_error::{proc_macro_error}; #[proc_macro] #[proc_macro_error] @@ -32,7 +30,10 @@ pub fn html_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream { fn expand(input: TokenStream) -> TokenStream { // TODO: call `proc_macro2::Span::mixed_site()` directly when Rust 1.45 is stable - let output_ident = TokenTree::Ident(Ident::new("__maud_output", proc_macro::Span::mixed_site().into())); + let output_ident = TokenTree::Ident(Ident::new( + "__maud_output", + proc_macro::Span::mixed_site().into(), + )); // Heuristic: the size of the resulting markup tends to correlate with the // code size of the template itself let size_hint = input.to_string().len(); diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs index e0b35ff..6390969 100644 --- a/maud_macros/src/parse.rs +++ b/maud_macros/src/parse.rs @@ -1,17 +1,9 @@ -use proc_macro2::{ - Delimiter, - Ident, - Literal, - Spacing, - Span, - TokenStream, - TokenTree, -}; +use proc_macro2::{Delimiter, Ident, Literal, Spacing, Span, TokenStream, TokenTree}; use proc_macro_error::{abort, abort_call_site, SpanRange}; use std::collections::HashMap; use std::mem; -use syn::{LitStr, parse_str}; +use syn::{parse_str, LitStr}; use crate::ast; @@ -83,14 +75,13 @@ impl Parser { match self.peek2() { None => break, Some((TokenTree::Punct(ref punct), _)) if punct.as_char() == ';' => self.advance(), - Some(( - TokenTree::Punct(ref punct), - Some(TokenTree::Ident(ref ident)), - )) if punct.as_char() == '@' && *ident == "let" => { + Some((TokenTree::Punct(ref punct), Some(TokenTree::Ident(ref ident)))) + if punct.as_char() == '@' && *ident == "let" => + { self.advance2(); let keyword = TokenTree::Ident(ident.clone()); result.push(self.let_expr(punct.span(), keyword)); - }, + } _ => result.push(self.markup()), } } @@ -103,14 +94,14 @@ impl Parser { Some(token) => token, None => { abort_call_site!("unexpected end of input"); - }, + } }; let markup = match token { // Literal TokenTree::Literal(lit) => { self.advance(); self.literal(&lit) - }, + } // Special form TokenTree::Punct(ref punct) if punct.as_char() == '@' => { self.advance(); @@ -123,25 +114,31 @@ impl Parser { let mut segments = Vec::new(); self.if_expr(at_span, vec![keyword], &mut segments); ast::Markup::Special { segments } - }, + } "while" => self.while_expr(at_span, keyword), "for" => self.for_expr(at_span, keyword), "match" => self.match_expr(at_span, keyword), "let" => { - let span = SpanRange { first: at_span, last: ident.span() }; + let span = SpanRange { + first: at_span, + last: ident.span(), + }; abort!(span, "`@let` only works inside a block"); - }, + } other => { - let span = SpanRange { first: at_span, last: ident.span() }; + let span = SpanRange { + first: at_span, + last: ident.span(), + }; abort!(span, "unknown keyword `@{}`", other); } } - }, + } _ => { abort!(at_span, "expected keyword after `@`"); - }, + } } - }, + } // Element TokenTree::Ident(ident) => { let ident_string = ident.to_string(); @@ -161,26 +158,29 @@ impl Parser { // already seen an `Ident` let name = self.try_namespaced_name().expect("identifier"); self.element(name) - }, + } // Div element shorthand TokenTree::Punct(ref punct) if punct.as_char() == '.' || punct.as_char() == '#' => { let name = TokenTree::Ident(Ident::new("div", punct.span())); self.element(name.into()) - }, + } // Splice TokenTree::Group(ref group) if group.delimiter() == Delimiter::Parenthesis => { self.advance(); - ast::Markup::Splice { expr: group.stream(), outer_span: SpanRange::single_span(group.span()) } + ast::Markup::Splice { + expr: group.stream(), + outer_span: SpanRange::single_span(group.span()), + } } // Block TokenTree::Group(ref group) if group.delimiter() == Delimiter::Brace => { self.advance(); ast::Markup::Block(self.block(group.stream(), SpanRange::single_span(group.span()))) - }, + } // ??? token => { abort!(token, "invalid syntax"); - }, + } }; markup } @@ -199,24 +199,19 @@ impl Parser { /// Parses an `@if` expression. /// /// The leading `@if` should already be consumed. - fn if_expr( - &mut self, - at_span: Span, - prefix: Vec<TokenTree>, - segments: &mut Vec<ast::Special>, - ) { + fn if_expr(&mut self, at_span: Span, prefix: Vec<TokenTree>, segments: &mut Vec<ast::Special>) { let mut head = prefix; let body = loop { match self.next() { Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { break self.block(block.stream(), SpanRange::single_span(block.span())); - }, + } Some(token) => head.push(token), None => { let mut span = ast::span_tokens(head); span.first = at_span; abort!(span, "expected body for this `@if`"); - }, + } } }; segments.push(ast::Special { @@ -232,10 +227,9 @@ impl Parser { /// The leading `@else if` or `@else` should *not* already be consumed. fn else_if_expr(&mut self, segments: &mut Vec<ast::Special>) { match self.peek2() { - Some(( - TokenTree::Punct(ref punct), - Some(TokenTree::Ident(ref else_keyword)), - )) if punct.as_char() == '@' && *else_keyword == "else" => { + Some((TokenTree::Punct(ref punct), Some(TokenTree::Ident(ref else_keyword)))) + if punct.as_char() == '@' && *else_keyword == "else" => + { self.advance2(); let at_span = punct.span(); let else_keyword = TokenTree::Ident(else_keyword.clone()); @@ -245,28 +239,32 @@ impl Parser { self.advance(); let if_keyword = TokenTree::Ident(if_keyword.clone()); self.if_expr(at_span, vec![else_keyword, if_keyword], segments) - }, + } // Just an `@else` - _ => { - match self.next() { - Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => { - let body = self.block(group.stream(), SpanRange::single_span(group.span())); - segments.push(ast::Special { - at_span: SpanRange::single_span(at_span), - head: vec![else_keyword].into_iter().collect(), - body, - }); - }, - _ => { - let span = SpanRange { first: at_span, last: else_keyword.span() }; - abort!(span, "expected body for this `@else`"); - }, + _ => match self.next() { + Some(TokenTree::Group(ref group)) + if group.delimiter() == Delimiter::Brace => + { + let body = + self.block(group.stream(), SpanRange::single_span(group.span())); + segments.push(ast::Special { + at_span: SpanRange::single_span(at_span), + head: vec![else_keyword].into_iter().collect(), + body, + }); + } + _ => { + let span = SpanRange { + first: at_span, + last: else_keyword.span(), + }; + abort!(span, "expected body for this `@else`"); } }, } - }, + } // We didn't find an `@else`; stop - _ => {}, + _ => {} } } @@ -280,16 +278,23 @@ impl Parser { match self.next() { Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { break self.block(block.stream(), SpanRange::single_span(block.span())); - }, + } Some(token) => head.push(token), None => { - let span = SpanRange { first: at_span, last: keyword_span }; + let span = SpanRange { + first: at_span, + last: keyword_span, + }; abort!(span, "expected body for this `@while`"); - }, + } } }; ast::Markup::Special { - segments: vec![ast::Special { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), body }], + segments: vec![ast::Special { + at_span: SpanRange::single_span(at_span), + head: head.into_iter().collect(), + body, + }], } } @@ -304,28 +309,38 @@ impl Parser { Some(TokenTree::Ident(ref in_keyword)) if *in_keyword == "in" => { head.push(TokenTree::Ident(in_keyword.clone())); break; - }, + } Some(token) => head.push(token), None => { - let span = SpanRange { first: at_span, last: keyword_span }; + let span = SpanRange { + first: at_span, + last: keyword_span, + }; abort!(span, "missing `in` in `@for` loop"); - }, + } } } let body = loop { match self.next() { Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => { break self.block(block.stream(), SpanRange::single_span(block.span())); - }, + } Some(token) => head.push(token), None => { - let span = SpanRange { first: at_span, last: keyword_span }; + let span = SpanRange { + first: at_span, + last: keyword_span, + }; abort!(span, "expected body for this `@for`"); - }, + } } }; ast::Markup::Special { - segments: vec![ast::Special { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), body }], + segments: vec![ast::Special { + at_span: SpanRange::single_span(at_span), + head: head.into_iter().collect(), + body, + }], } } @@ -340,15 +355,23 @@ impl Parser { Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => { let span = SpanRange::single_span(body.span()); break (self.with_input(body.stream()).match_arms(), span); - }, + } Some(token) => head.push(token), None => { - let span = SpanRange { first: at_span, last: keyword_span }; + let span = SpanRange { + first: at_span, + last: keyword_span, + }; abort!(span, "expected body for this `@match`"); - }, + } } }; - ast::Markup::Match { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), arms, arms_span } + ast::Markup::Match { + at_span: SpanRange::single_span(at_span), + head: head.into_iter().collect(), + arms, + arms_span, + } } fn match_arms(&mut self) -> Vec<ast::MatchArm> { @@ -364,16 +387,19 @@ impl Parser { loop { match self.peek2() { Some((TokenTree::Punct(ref eq), Some(TokenTree::Punct(ref gt)))) - if eq.as_char() == '=' && gt.as_char() == '>' && eq.spacing() == Spacing::Joint => { + if eq.as_char() == '=' + && gt.as_char() == '>' + && eq.spacing() == Spacing::Joint => + { self.advance2(); head.push(TokenTree::Punct(eq.clone())); head.push(TokenTree::Punct(gt.clone())); break; - }, + } Some((token, _)) => { self.advance(); head.push(token); - }, + } None => { if head.is_empty() { return None; @@ -381,7 +407,7 @@ impl Parser { let head_span = ast::span_tokens(head); abort!(head_span, "unexpected end of @match pattern"); } - }, + } } } let body = match self.next() { @@ -395,7 +421,7 @@ impl Parser { } } body - }, + } // $pat => $expr Some(first_token) => { let mut span = SpanRange::single_span(first_token.span()); @@ -406,18 +432,21 @@ impl Parser { Some(token) => { span.last = token.span(); body.push(token); - }, + } None => break, } } self.block(body.into_iter().collect(), span) - }, + } None => { let span = ast::span_tokens(head); abort!(span, "unexpected end of @match arm"); - }, + } }; - Some(ast::MatchArm { head: head.into_iter().collect(), body }) + Some(ast::MatchArm { + head: head.into_iter().collect(), + body, + }) } /// Parses a `@let` expression. @@ -427,14 +456,12 @@ impl Parser { let mut tokens = vec![keyword]; loop { match self.next() { - Some(token) => { - match token { - TokenTree::Punct(ref punct) if punct.as_char() == '=' => { - tokens.push(token.clone()); - break; - }, - _ => tokens.push(token), + Some(token) => match token { + TokenTree::Punct(ref punct) if punct.as_char() == '=' => { + tokens.push(token.clone()); + break; } + _ => tokens.push(token), }, None => { let mut span = ast::span_tokens(tokens); @@ -445,14 +472,12 @@ impl Parser { } loop { match self.next() { - Some(token) => { - match token { - TokenTree::Punct(ref punct) if punct.as_char() == ';' => { - tokens.push(token.clone()); - break; - }, - _ => tokens.push(token), + Some(token) => match token { + TokenTree::Punct(ref punct) if punct.as_char() == ';' => { + tokens.push(token.clone()); + break; } + _ => tokens.push(token), }, None => { let mut span = ast::span_tokens(tokens); @@ -462,10 +487,13 @@ impl Parser { "unexpected end of `@let` expression"; help = "are you missing a semicolon?" ); - }, + } } } - ast::Markup::Let { at_span: SpanRange::single_span(at_span), tokens: tokens.into_iter().collect() } + ast::Markup::Let { + at_span: SpanRange::single_span(at_span), + tokens: tokens.into_iter().collect(), + } } /// Parses an element node. @@ -479,22 +507,23 @@ impl Parser { let attrs = self.attrs(); let body = match self.peek() { Some(TokenTree::Punct(ref punct)) - if punct.as_char() == ';' || punct.as_char() == '/' => { + if punct.as_char() == ';' || punct.as_char() == '/' => + { // Void element self.advance(); - ast::ElementBody::Void { semi_span: SpanRange::single_span(punct.span()) } - }, - _ => { - match self.markup() { - ast::Markup::Block(block) => ast::ElementBody::Block { block }, - markup => { - let markup_span = markup.span(); - abort!( - markup_span, - "element body must be wrapped in braces"; - help = "see https://github.com/lambda-fairy/maud/pull/137 for details" - ); - }, + ast::ElementBody::Void { + semi_span: SpanRange::single_span(punct.span()), + } + } + _ => match self.markup() { + ast::Markup::Block(block) => ast::ElementBody::Block { block }, + markup => { + let markup_span = markup.span(); + abort!( + markup_span, + "element body must be wrapped in braces"; + help = "see https://github.com/lambda-fairy/maud/pull/137 for details" + ); } }, }; @@ -525,7 +554,7 @@ impl Parser { attr_type: ast::AttrType::Normal { value }, }, }); - }, + } // Empty attribute (Some(ref name), Some(TokenTree::Punct(ref punct))) if punct.as_char() == '?' => { self.commit(attempt); @@ -536,20 +565,27 @@ impl Parser { attr_type: ast::AttrType::Empty { toggler }, }, }); - }, + } // Class shorthand (None, Some(TokenTree::Punct(ref punct))) if punct.as_char() == '.' => { self.commit(attempt); let name = self.class_or_id_name(); let toggler = self.attr_toggler(); - attrs.push(ast::Attr::Class { dot_span: SpanRange::single_span(punct.span()), name, toggler }); - }, + attrs.push(ast::Attr::Class { + dot_span: SpanRange::single_span(punct.span()), + name, + toggler, + }); + } // ID shorthand (None, Some(TokenTree::Punct(ref punct))) if punct.as_char() == '#' => { self.commit(attempt); let name = self.class_or_id_name(); - attrs.push(ast::Attr::Id { hash_span: SpanRange::single_span(punct.span()), name }); - }, + attrs.push(ast::Attr::Id { + hash_span: SpanRange::single_span(punct.span()), + name, + }); + } // If it's not a valid attribute, backtrack and bail out _ => break, } @@ -566,11 +602,14 @@ impl Parser { } has_class = true; "class".to_string() - }, + } ast::Attr::Id { .. } => "id".to_string(), - ast::Attr::Attribute { attribute } => { - attribute.name.clone().into_iter().map(|token| token.to_string()).collect() - }, + ast::Attr::Attribute { attribute } => attribute + .name + .clone() + .into_iter() + .map(|token| token.to_string()) + .collect(), }; let entry = attr_map.entry(name).or_default(); entry.push(attr.span()); @@ -605,7 +644,7 @@ impl Parser { cond: group.stream(), cond_span: SpanRange::single_span(group.span()), }) - }, + } _ => None, } } @@ -626,12 +665,12 @@ impl Parser { self.advance(); result.push(TokenTree::Punct(punct.clone())); true - }, + } Some(TokenTree::Ident(ref ident)) if expect_ident => { self.advance(); result.push(TokenTree::Ident(ident.clone())); false - }, + } _ => break, }; } @@ -655,6 +694,9 @@ impl Parser { /// Parses the given token stream as a Maud expression. fn block(&mut self, body: TokenStream, outer_span: SpanRange) -> ast::Block { let markups = self.with_input(body).markups(); - ast::Block { markups, outer_span } + ast::Block { + markups, + outer_span, + } } }