diff --git a/CHANGELOG.md b/CHANGELOG.md index c1d91020..60f636d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ [#377](https://github.com/lambda-fairy/maud/pull/377) - Implement `Render` for `Arc` [#380](https://github.com/lambda-fairy/maud/pull/380) +- Accept literals in attribute names + [#396](https://github.com/lambda-fairy/maud/pull/396) ## [0.25.0] - 2023-04-16 diff --git a/maud/tests/basic_syntax.rs b/maud/tests/basic_syntax.rs index b84f7951..96e0235e 100644 --- a/maud/tests/basic_syntax.rs +++ b/maud/tests/basic_syntax.rs @@ -181,6 +181,34 @@ fn hyphens_in_attribute_names() { ); } +#[test] +fn string_literals_in_attribute_names() { + let result = html! { this "@sentence:-is.not"="false" of-course {} }; + assert_eq!( + result.into_string(), + r#""# + ); +} + +#[test] +fn raw_string_literals_in_attribute_names() { + let result = html! { this r#"@sentence:-is.not"#="false" of-course {} }; + assert_eq!( + result.into_string(), + r#""# + ); +} + +#[test] +fn other_literals_in_attribute_names() { + let result = + html! { this b"byte_string"="false" 123="123" 2.5 true 'a'="a" b'b'="b" of-course {} }; + assert_eq!( + result.into_string(), + r#""# + ); +} + #[test] fn class_shorthand() { let result = html! { p { "Hi, " span.name { "Lyra" } "!" } }; diff --git a/maud_macros/src/ast.rs b/maud_macros/src/ast.rs index cd8a2cef..16d228e3 100644 --- a/maud_macros/src/ast.rs +++ b/maud_macros/src/ast.rs @@ -1,5 +1,6 @@ use proc_macro2::{TokenStream, TokenTree}; use proc_macro_error::SpanRange; +use syn::Lit; #[derive(Debug)] pub enum Markup { @@ -217,5 +218,21 @@ pub fn join_ranges>(ranges: I) -> SpanRange { } pub fn name_to_string(name: TokenStream) -> String { - name.into_iter().map(|token| token.to_string()).collect() + name.into_iter() + .map(|token| { + if let TokenTree::Literal(literal) = token { + match Lit::new(literal.clone()) { + Lit::Str(str) => str.value(), + Lit::Char(char) => char.value().to_string(), + Lit::ByteStr(byte) => { + String::from_utf8(byte.value()).expect("Invalid utf8 byte") + } + Lit::Byte(byte) => (byte.value() as char).to_string(), + _ => literal.to_string(), + } + } else { + token.to_string() + } + }) + .collect() } diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs index 3521ce42..662b8309 100644 --- a/maud_macros/src/parse.rs +++ b/maud_macros/src/parse.rs @@ -702,12 +702,13 @@ impl Parser { /// Parses an identifier, without dealing with namespaces. fn try_name(&mut self) -> Option { let mut result = Vec::new(); - if let Some(token @ TokenTree::Ident(_)) = self.peek() { - self.advance(); - result.push(token); - } else { - return None; - } + match self.peek() { + Some(token @ TokenTree::Ident(_)) | Some(token @ TokenTree::Literal(_)) => { + self.advance(); + result.push(token); + } + _ => return None, + }; let mut expect_ident = false; loop { expect_ident = match self.peek() {