Drop the bad Rust 2018 Cargo forces on you. Rewrite to work with actual native proc_macros.
This commit is contained in:
parent
f7ce896ca3
commit
858b16cf34
|
@ -2,12 +2,9 @@
|
||||||
name = "typed-html-macros"
|
name = "typed-html-macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Bodil Stokke <bodil@bodil.org>"]
|
authors = ["Bodil Stokke <bodil@bodil.org>"]
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "0.6.8"
|
|
||||||
pom = "2.0.1"
|
pom = "2.0.1"
|
||||||
proc-macro2 = "0.4.20"
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro::{Ident, Span, TokenStream};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
use map::StringyMap;
|
||||||
|
|
||||||
pub fn required_children(element: &str) -> &[&str] {
|
pub fn required_children(element: &str) -> &[&str] {
|
||||||
match element {
|
match element {
|
||||||
|
@ -9,10 +10,13 @@ pub fn required_children(element: &str) -> &[&str] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn global_attrs(span: Span) -> HashMap<Ident, TokenStream> {
|
pub fn global_attrs(span: Span) -> StringyMap<Ident, TokenStream> {
|
||||||
let mut attrs = HashMap::new();
|
let mut attrs = StringyMap::new();
|
||||||
let mut insert = |key, value: &str| attrs.insert(Ident::new(key, span), value.parse().unwrap());
|
{
|
||||||
insert("id", "crate::elements::CssId");
|
let mut insert =
|
||||||
insert("class", "crate::elements::CssClass");
|
|key, value: &str| attrs.insert(Ident::new(key, span), value.parse().unwrap());
|
||||||
|
insert("id", "crate::elements::CssId");
|
||||||
|
insert("class", "crate::elements::CssClass");
|
||||||
|
}
|
||||||
attrs
|
attrs
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
use pom::combinator::*;
|
use pom::combinator::*;
|
||||||
use pom::Parser;
|
use pom::Parser;
|
||||||
use proc_macro2::{Group, Ident, TokenStream, TokenTree};
|
use proc_macro::{quote, Group, Ident, Literal, TokenStream, TokenTree};
|
||||||
use quote::quote;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::config::global_attrs;
|
use config::global_attrs;
|
||||||
use crate::parser::*;
|
use map::StringyMap;
|
||||||
|
use parser::*;
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
struct Declare {
|
struct Declare {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
attrs: HashMap<Ident, TokenStream>,
|
attrs: StringyMap<Ident, TokenStream>,
|
||||||
req_children: Vec<Ident>,
|
req_children: Vec<Ident>,
|
||||||
opt_children: Option<TokenStream>,
|
opt_children: Option<TokenStream>,
|
||||||
traits: Vec<TokenStream>,
|
traits: Vec<TokenStream>,
|
||||||
|
@ -28,37 +27,33 @@ impl Declare {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elem_name(&self) -> Ident {
|
fn elem_name(&self) -> TokenTree {
|
||||||
Ident::new(
|
Ident::new(
|
||||||
&format!("Element_{}", self.name.to_string()),
|
&format!("Element_{}", self.name.to_string()),
|
||||||
self.name.span(),
|
self.name.span(),
|
||||||
)
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attr_names(&self) -> impl Iterator<Item = Ident> + '_ {
|
fn attrs(&self) -> impl Iterator<Item = (TokenTree, TokenStream, TokenTree)> + '_ {
|
||||||
self.attrs
|
self.attrs.iter().map(|(key, value)| {
|
||||||
.keys()
|
let attr_name: TokenTree =
|
||||||
.map(|k| Ident::new(&format!("attr_{}", k.to_string()), k.span()))
|
Ident::new(&format!("attr_{}", key.to_string()), key.span()).into();
|
||||||
|
let attr_type = value.clone();
|
||||||
|
let attr_str = Literal::string(&key.to_string()).into();
|
||||||
|
(attr_name, attr_type, attr_str)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attr_names_str(&self) -> impl Iterator<Item = String> + '_ {
|
fn req_children(&self) -> impl Iterator<Item = (TokenTree, TokenTree, TokenTree)> + '_ {
|
||||||
self.attrs.keys().map(|k| k.to_string())
|
self.req_children.iter().map(|child| {
|
||||||
}
|
let child_name: TokenTree =
|
||||||
|
Ident::new(&format!("child_{}", child.to_string()), child.span()).into();
|
||||||
fn req_child_names(&self) -> impl Iterator<Item = Ident> + '_ {
|
let child_type: TokenTree =
|
||||||
self.req_children
|
Ident::new(&format!("Element_{}", child.to_string()), child.span()).into();
|
||||||
.iter()
|
let child_str = Literal::string(&child.to_string()).into();
|
||||||
.map(|c| Ident::new(&format!("child_{}", c.to_string()), c.span()))
|
(child_name, child_type, child_str)
|
||||||
}
|
})
|
||||||
|
|
||||||
fn req_child_names_str(&self) -> impl Iterator<Item = String> + '_ {
|
|
||||||
self.req_children.iter().map(|i| i.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn req_child_types(&self) -> impl Iterator<Item = Ident> + '_ {
|
|
||||||
self.req_children
|
|
||||||
.iter()
|
|
||||||
.map(|c| Ident::new(&format!("Element_{}", c.to_string()), c.span()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_token_stream(self) -> TokenStream {
|
fn into_token_stream(self) -> TokenStream {
|
||||||
|
@ -74,46 +69,58 @@ impl Declare {
|
||||||
|
|
||||||
fn struct_(&self) -> TokenStream {
|
fn struct_(&self) -> TokenStream {
|
||||||
let elem_name = self.elem_name();
|
let elem_name = self.elem_name();
|
||||||
let attr_name = self.attr_names();
|
|
||||||
let attr_type = self.attrs.values();
|
|
||||||
let req_child_name = self.req_child_names();
|
|
||||||
let req_child_type = self.req_child_types();
|
|
||||||
|
|
||||||
let children = match &self.opt_children {
|
let mut body = TokenStream::new();
|
||||||
Some(child_constraint) => quote!(pub children: Vec<Box<#child_constraint>>),
|
|
||||||
None => TokenStream::new(),
|
for (attr_name, attr_type, _) in self.attrs() {
|
||||||
};
|
body.extend(quote!( pub $attr_name: Option<$attr_type>, ));
|
||||||
|
}
|
||||||
|
|
||||||
|
body.extend(quote!(
|
||||||
|
pub data_attributes: std::collections::BTreeMap<String, String>,
|
||||||
|
));
|
||||||
|
|
||||||
|
for (child_name, child_type, _) in self.req_children() {
|
||||||
|
body.extend(quote!( pub $child_name: Box<$child_type>, ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(child_constraint) = &self.opt_children {
|
||||||
|
let child_constraint = child_constraint.clone();
|
||||||
|
body.extend(quote!(pub children: Vec<Box<$child_constraint>>,));
|
||||||
|
}
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
pub struct #elem_name {
|
pub struct $elem_name {
|
||||||
#( pub #attr_name: Option<#attr_type>, )*
|
$body
|
||||||
pub data_attributes: std::collections::BTreeMap<String, String>,
|
|
||||||
#( pub #req_child_name: Box<#req_child_type>, )*
|
|
||||||
#children
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_(&self) -> TokenStream {
|
fn impl_(&self) -> TokenStream {
|
||||||
let elem_name = self.elem_name();
|
let elem_name = self.elem_name();
|
||||||
let req_child_name = self.req_child_names();
|
|
||||||
let req_child_type = self.req_child_types();
|
|
||||||
let req_child_name_again = self.req_child_names();
|
|
||||||
let attr_name = self.attr_names();
|
|
||||||
|
|
||||||
let construct_children = match self.opt_children {
|
let mut args = TokenStream::new();
|
||||||
Some(_) => quote!(children: Vec::new()),
|
for (child_name, child_type, _) in self.req_children() {
|
||||||
None => TokenStream::new(),
|
args.extend(quote!( $child_name: Box<$child_type>, ));
|
||||||
};
|
}
|
||||||
|
|
||||||
|
let mut body = TokenStream::new();
|
||||||
|
for (attr_name, _, _) in self.attrs() {
|
||||||
|
body.extend(quote!( $attr_name: None, ));
|
||||||
|
}
|
||||||
|
body.extend(quote!(data_attributes: std::collections::BTreeMap::new(),));
|
||||||
|
for (child_name, _, _) in self.req_children() {
|
||||||
|
body.extend(quote!( $child_name, ));
|
||||||
|
}
|
||||||
|
if self.opt_children.is_some() {
|
||||||
|
body.extend(quote!(children: Vec::new()));
|
||||||
|
}
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
impl #elem_name {
|
impl $elem_name {
|
||||||
pub fn new(#(#req_child_name: Box<#req_child_type>),*) -> Self {
|
pub fn new($args) -> Self {
|
||||||
#elem_name {
|
$elem_name {
|
||||||
#( #attr_name: None, )*
|
$body
|
||||||
data_attributes: std::collections::BTreeMap::new(),
|
|
||||||
#( #req_child_name_again, )*
|
|
||||||
#construct_children
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,43 +130,47 @@ impl Declare {
|
||||||
fn impl_node(&self) -> TokenStream {
|
fn impl_node(&self) -> TokenStream {
|
||||||
let elem_name = self.elem_name();
|
let elem_name = self.elem_name();
|
||||||
quote!(
|
quote!(
|
||||||
impl Node for #elem_name {}
|
impl ::elements::Node for $elem_name {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_element(&self) -> TokenStream {
|
fn impl_element(&self) -> TokenStream {
|
||||||
let elem_name = self.elem_name();
|
let elem_name = self.elem_name();
|
||||||
let attr_name_str = self.attr_names_str();
|
|
||||||
let req_child_str_name = self.req_child_names_str();
|
let attrs: TokenStream = self.attrs().map(|(_, _, name)| quote!( $name, )).collect();
|
||||||
|
let reqs: TokenStream = self
|
||||||
|
.req_children()
|
||||||
|
.map(|(_, _, name)| quote!( $name, ))
|
||||||
|
.collect();
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
impl Element for #elem_name {
|
impl ::elements::Element for $elem_name {
|
||||||
fn attributes() -> &'static [&'static str] {
|
fn attributes() -> &'static [&'static str] {
|
||||||
&[ #(#attr_name_str),* ]
|
&[ $attrs ]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_children() -> &'static [&'static str] {
|
fn required_children() -> &'static [&'static str] {
|
||||||
&[ #(#req_child_str_name),* ]
|
&[ $reqs ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_marker_traits(&self) -> TokenStream {
|
fn impl_marker_traits(&self) -> TokenStream {
|
||||||
let trait_for = std::iter::repeat(self.elem_name());
|
let elem_name = self.elem_name();
|
||||||
let trait_name = self.traits.iter();
|
let mut body = TokenStream::new();
|
||||||
quote!(
|
for t in &self.traits {
|
||||||
#(
|
let name = t.clone();
|
||||||
impl #trait_name for #trait_for {}
|
body.extend(quote!(
|
||||||
)*
|
impl $name for $elem_name {}
|
||||||
)
|
));
|
||||||
|
}
|
||||||
|
body
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_display(&self) -> TokenStream {
|
fn impl_display(&self) -> TokenStream {
|
||||||
let elem_name = self.elem_name();
|
let elem_name = self.elem_name();
|
||||||
let name = self.name.to_string();
|
let name: TokenTree = Literal::string(&self.name.to_string()).into();
|
||||||
let attr_name = self.attr_names();
|
|
||||||
let attr_name_str = self.attr_names_str();
|
|
||||||
let req_child_name: Vec<_> = self.req_child_names().collect();
|
|
||||||
|
|
||||||
let print_opt_children = if self.opt_children.is_some() {
|
let print_opt_children = if self.opt_children.is_some() {
|
||||||
quote!(for child in &self.children {
|
quote!(for child in &self.children {
|
||||||
|
@ -168,14 +179,22 @@ impl Declare {
|
||||||
} else {
|
} else {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
let print_children = if req_child_name.is_empty() {
|
|
||||||
|
let mut print_req_children = TokenStream::new();
|
||||||
|
for (child_name, _, _) in self.req_children() {
|
||||||
|
print_req_children.extend(quote!(
|
||||||
|
self.$child_name.fmt(f)?;
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let print_children = if self.req_children.is_empty() {
|
||||||
if self.opt_children.is_some() {
|
if self.opt_children.is_some() {
|
||||||
quote!(if self.children.is_empty() {
|
quote!(if self.children.is_empty() {
|
||||||
write!(f, "/>")
|
write!(f, "/>")
|
||||||
} else {
|
} else {
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
#print_opt_children
|
$print_opt_children
|
||||||
write!(f, "</{}>", #name)
|
write!(f, "</{}>", $name)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
quote!(write!(f, "/>"))
|
quote!(write!(f, "/>"))
|
||||||
|
@ -183,27 +202,30 @@ impl Declare {
|
||||||
} else {
|
} else {
|
||||||
quote!(
|
quote!(
|
||||||
write!(f, ">")?;
|
write!(f, ">")?;
|
||||||
#(
|
$print_req_children
|
||||||
self.#req_child_name.fmt(f)?;
|
$print_opt_children
|
||||||
)*
|
write!(f, "</{}>", $name)
|
||||||
#print_opt_children
|
|
||||||
write!(f, "</{}>", #name)
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut print_attrs = TokenStream::new();
|
||||||
|
for (attr_name, _, attr_str) in self.attrs() {
|
||||||
|
print_attrs.extend(quote!(
|
||||||
|
if let Some(ref value) = self.$attr_name {
|
||||||
|
write!(f, " {}={:?}", $attr_str, value.to_string())?;
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
impl std::fmt::Display for #elem_name {
|
impl std::fmt::Display for $elem_name {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||||
write!(f, "<{}", #name);
|
write!(f, "<{}", $name);
|
||||||
#(
|
$print_attrs
|
||||||
if let Some(ref value) = self.#attr_name {
|
|
||||||
write!(f, " {}={:?}", #attr_name_str, value.to_string())?;
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
for (key, value) in &self.data_attributes {
|
for (key, value) in &self.data_attributes {
|
||||||
write!(f, " data-{}={:?}", key, value)?;
|
write!(f, " data-{}={:?}", key, value)?;
|
||||||
}
|
}
|
||||||
#print_children
|
$print_children
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -214,10 +236,13 @@ impl Declare {
|
||||||
|
|
||||||
fn declare_attrs<'a>() -> Combinator<impl Parser<'a, TokenTree, Output = Vec<(Ident, TokenStream)>>>
|
fn declare_attrs<'a>() -> Combinator<impl Parser<'a, TokenTree, Output = Vec<(Ident, TokenStream)>>>
|
||||||
{
|
{
|
||||||
group().map(|group: Group| {
|
group().map(|group: Group| -> Vec<(Ident, TokenStream)> {
|
||||||
let attr = ident() - punct(':') + type_spec();
|
let attr = ident() - punct(':') + type_spec();
|
||||||
|
let parser = attr.repeat(0..);
|
||||||
let input: Vec<TokenTree> = group.stream().into_iter().collect();
|
let input: Vec<TokenTree> = group.stream().into_iter().collect();
|
||||||
let result = attr.repeat(0..).parse(&input);
|
// FIXME the borrow checker won't let me use plain &input, it seems like a bug.
|
||||||
|
// It works in Rust 2018, so please get rid of this unsafe block when it stabilises.
|
||||||
|
let result = parser.parse(unsafe { &*(input.as_slice() as *const _) });
|
||||||
result.unwrap()
|
result.unwrap()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use pom::combinator::*;
|
use pom::combinator::*;
|
||||||
use pom::Parser;
|
use pom::Parser;
|
||||||
use proc_macro2::{Group, Ident, Literal, TokenStream, TokenTree};
|
use proc_macro::{quote, Group, Ident, Literal, TokenStream, TokenTree};
|
||||||
use quote::quote;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use crate::config::required_children;
|
use config::required_children;
|
||||||
use crate::parser::*;
|
use map::StringyMap;
|
||||||
|
use parser::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum Node {
|
enum Node {
|
||||||
|
@ -19,7 +18,8 @@ impl Node {
|
||||||
match self {
|
match self {
|
||||||
Node::Element(el) => el.into_token_stream(),
|
Node::Element(el) => el.into_token_stream(),
|
||||||
Node::Text(text) => {
|
Node::Text(text) => {
|
||||||
quote!(Box::new(typed_html::elements::TextNode::new(#text.to_string())))
|
let text = TokenTree::Literal(text);
|
||||||
|
quote!(Box::new(typed_html::elements::TextNode::new($text.to_string())))
|
||||||
}
|
}
|
||||||
Node::Block(_) => panic!("cannot have a block in this position"),
|
Node::Block(_) => panic!("cannot have a block in this position"),
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,23 @@ impl Node {
|
||||||
Node::Element(el) => {
|
Node::Element(el) => {
|
||||||
let el = el.into_token_stream();
|
let el = el.into_token_stream();
|
||||||
quote!(
|
quote!(
|
||||||
element.children.push(#el);
|
element.children.push($el);
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
tx @ Node::Text(_) => {
|
tx @ Node::Text(_) => {
|
||||||
let tx = tx.into_token_stream();
|
let tx = tx.into_token_stream();
|
||||||
quote!(
|
quote!(
|
||||||
element.children.push(#tx);
|
element.children.push($tx);
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Node::Block(group) => quote!(
|
Node::Block(group) => {
|
||||||
for child in #group.into_iter() {
|
let group: TokenTree = group.into();
|
||||||
|
quote!(
|
||||||
|
for child in $group.into_iter() {
|
||||||
element.children.push(child);
|
element.children.push(child);
|
||||||
}
|
}
|
||||||
),
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,12 +54,12 @@ impl Node {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Element {
|
struct Element {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
attributes: BTreeMap<Ident, TokenTree>,
|
attributes: StringyMap<Ident, TokenTree>,
|
||||||
children: Vec<Node>,
|
children: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_data_attrs(attrs: &mut BTreeMap<Ident, TokenTree>) -> BTreeMap<String, TokenTree> {
|
fn extract_data_attrs(attrs: &mut StringyMap<Ident, TokenTree>) -> StringyMap<String, TokenTree> {
|
||||||
let mut data = BTreeMap::new();
|
let mut data = StringyMap::new();
|
||||||
let keys: Vec<Ident> = attrs.keys().cloned().collect();
|
let keys: Vec<Ident> = attrs.keys().cloned().collect();
|
||||||
for key in keys {
|
for key in keys {
|
||||||
let key_name = key.to_string();
|
let key_name = key.to_string();
|
||||||
|
@ -73,7 +76,7 @@ impl Element {
|
||||||
fn new(name: Ident) -> Self {
|
fn new(name: Ident) -> Self {
|
||||||
Element {
|
Element {
|
||||||
name,
|
name,
|
||||||
attributes: BTreeMap::new(),
|
attributes: StringyMap::new(),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +84,7 @@ impl Element {
|
||||||
fn into_token_stream(mut self) -> TokenStream {
|
fn into_token_stream(mut self) -> TokenStream {
|
||||||
let name = self.name;
|
let name = self.name;
|
||||||
let name_str = name.to_string();
|
let name_str = name.to_string();
|
||||||
let typename = Ident::new(&format!("Element_{}", &name_str), name.span());
|
let typename: TokenTree = Ident::new(&format!("Element_{}", &name_str), name.span()).into();
|
||||||
let req_names = required_children(&name_str);
|
let req_names = required_children(&name_str);
|
||||||
if req_names.len() > self.children.len() {
|
if req_names.len() > self.children.len() {
|
||||||
panic!(
|
panic!(
|
||||||
|
@ -92,34 +95,44 @@ impl Element {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let data_attrs = extract_data_attrs(&mut self.attributes);
|
let data_attrs = extract_data_attrs(&mut self.attributes);
|
||||||
let data_keys = data_attrs.keys().cloned();
|
let attrs = self.attributes.iter().map(|(key, value)| {
|
||||||
let data_values = data_attrs.values().cloned();
|
(
|
||||||
let keys: Vec<_> = self
|
TokenTree::Ident(Ident::new(&format!("attr_{}", key), key.span())),
|
||||||
.attributes
|
value.clone(),
|
||||||
.keys()
|
)
|
||||||
.map(|key| Ident::new(&format!("attr_{}", key), key.span()))
|
});
|
||||||
.collect();
|
|
||||||
let values: Vec<TokenTree> = self.attributes.values().cloned().collect();
|
|
||||||
let opt_children = self
|
let opt_children = self
|
||||||
.children
|
.children
|
||||||
.split_off(req_names.len())
|
.split_off(req_names.len())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Node::into_child_stream);
|
.map(Node::into_child_stream);
|
||||||
let req_children = self.children.into_iter().map(Node::into_token_stream);
|
let req_children = self.children.into_iter().map(Node::into_token_stream);
|
||||||
|
|
||||||
|
let mut body = TokenStream::new();
|
||||||
|
for (key, value) in attrs {
|
||||||
|
body.extend(quote!(
|
||||||
|
element.$key = Some($value.into());
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for (key, value) in data_attrs
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (TokenTree::from(Literal::string(&k)), v.clone()))
|
||||||
|
{
|
||||||
|
body.extend(quote!(
|
||||||
|
element.data_attributes.insert($key.into(), $value.into());
|
||||||
|
));
|
||||||
|
}
|
||||||
|
body.extend(opt_children);
|
||||||
|
|
||||||
|
let mut args = TokenStream::new();
|
||||||
|
for arg in req_children {
|
||||||
|
args.extend(quote!( $arg, ));
|
||||||
|
}
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
{
|
{
|
||||||
let mut element = typed_html::elements::#typename::new(
|
let mut element = typed_html::elements::$typename::new($args);
|
||||||
#({ #req_children }),*
|
$body
|
||||||
);
|
|
||||||
#(
|
|
||||||
element.#keys = Some(#values.into());
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
element.data_attributes.insert(#data_keys.into(), #data_values.into());
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
#opt_children
|
|
||||||
)*
|
|
||||||
Box::new(element)
|
Box::new(element)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,32 +1,34 @@
|
||||||
|
#![feature(proc_macro_hygiene)]
|
||||||
|
#![feature(proc_macro_quote)]
|
||||||
#![feature(proc_macro_span)]
|
#![feature(proc_macro_span)]
|
||||||
|
|
||||||
|
extern crate pom;
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use proc_macro2::{TokenStream, TokenTree};
|
use proc_macro::{TokenStream, TokenTree};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod declare;
|
mod declare;
|
||||||
mod html;
|
mod html;
|
||||||
|
mod map;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn html(input: TokenStream) -> TokenStream {
|
||||||
let input: TokenStream = input.into();
|
|
||||||
let input: Vec<TokenTree> = input.into_iter().collect();
|
let input: Vec<TokenTree> = input.into_iter().collect();
|
||||||
let result = html::expand_html(&input);
|
let result = html::expand_html(&input);
|
||||||
match result {
|
match result {
|
||||||
Err(error) => panic!(parser::parse_error(&input, &error)),
|
Err(error) => panic!(parser::parse_error(&input, &error)),
|
||||||
Ok(ts) => ts.into(),
|
Ok(ts) => ts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn declare_element(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn declare_element(input: TokenStream) -> TokenStream {
|
||||||
let input: TokenStream = input.into();
|
|
||||||
let input: Vec<TokenTree> = input.into_iter().collect();
|
let input: Vec<TokenTree> = input.into_iter().collect();
|
||||||
let result = declare::expand_declare(&input);
|
let result = declare::expand_declare(&input);
|
||||||
match result {
|
match result {
|
||||||
Err(error) => panic!(parser::parse_error(&input, &error)),
|
Err(error) => panic!(parser::parse_error(&input, &error)),
|
||||||
Ok(ts) => ts.into(),
|
Ok(ts) => ts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StringyMap<K, V>(BTreeMap<String, (K, V)>);
|
||||||
|
|
||||||
|
impl<K, V> StringyMap<K, V>
|
||||||
|
where
|
||||||
|
K: ToString,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
StringyMap(BTreeMap::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||||
|
let s = k.to_string();
|
||||||
|
self.0.insert(s, (k, v)).map(|(_, v)| v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, k: &K) -> Option<V> {
|
||||||
|
let s = k.to_string();
|
||||||
|
self.0.remove(&s).map(|(_, v)| v)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &(K, V)> {
|
||||||
|
self.0.values()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn keys(&self) -> impl Iterator<Item = &K> {
|
||||||
|
self.0.values().map(|(k, _)| k)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use pom::combinator::*;
|
use pom::combinator::*;
|
||||||
use pom::{Error, Parser};
|
use pom::{Error, Parser};
|
||||||
use proc_macro2::{Group, Ident, Literal, Punct, TokenStream, TokenTree};
|
use proc_macro::{Group, Ident, Literal, Punct, TokenStream, TokenTree};
|
||||||
|
|
||||||
pub fn unit<'a, I: 'a, A: Clone>(value: A) -> Combinator<impl Parser<'a, I, Output = A>> {
|
pub fn unit<'a, I: 'a, A: Clone>(value: A) -> Combinator<impl Parser<'a, I, Output = A>> {
|
||||||
comb(move |_, start| Ok((value.clone(), start)))
|
comb(move |_, start| Ok((value.clone(), start)))
|
||||||
|
@ -29,7 +29,7 @@ pub fn ident<'a>() -> Combinator<impl Parser<'a, TokenTree, Output = Ident>> {
|
||||||
pub fn ident_match<'a>(name: String) -> Combinator<impl Parser<'a, TokenTree, Output = ()>> {
|
pub fn ident_match<'a>(name: String) -> Combinator<impl Parser<'a, TokenTree, Output = ()>> {
|
||||||
comb(move |input: &[TokenTree], start| match input.get(start) {
|
comb(move |input: &[TokenTree], start| match input.get(start) {
|
||||||
Some(TokenTree::Ident(i)) => {
|
Some(TokenTree::Ident(i)) => {
|
||||||
if *i == name {
|
if i.to_string() == name {
|
||||||
Ok(((), start + 1))
|
Ok(((), start + 1))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::Mismatch {
|
Err(Error::Mismatch {
|
||||||
|
@ -93,8 +93,7 @@ pub fn html_ident<'a>() -> Combinator<impl Parser<'a, TokenTree, Output = Ident>
|
||||||
(
|
(
|
||||||
match span {
|
match span {
|
||||||
None => Some(token.span()),
|
None => Some(token.span()),
|
||||||
// FIXME: Some(span) => Some(span.join(token.span())),
|
Some(span) => span.join(token.span()),
|
||||||
span => span,
|
|
||||||
},
|
},
|
||||||
match token {
|
match token {
|
||||||
TokenTree::Ident(ident) => name + &ident.to_string(),
|
TokenTree::Ident(ident) => name + &ident.to_string(),
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
name = "typed-html"
|
name = "typed-html"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Bodil Stokke <bodil@bodil.org>"]
|
authors = ["Bodil Stokke <bodil@bodil.org>"]
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
typed-html-macros = { path = "../macros" }
|
typed-html-macros = { path = "../macros" }
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate typed_html;
|
extern crate typed_html;
|
||||||
|
extern crate typed_html_macros;
|
||||||
|
|
||||||
use typed_html_macros::html;
|
use typed_html_macros::html;
|
||||||
|
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
|
extern crate http;
|
||||||
|
extern crate typed_html_macros;
|
||||||
|
|
||||||
pub mod elements;
|
pub mod elements;
|
||||||
|
|
Loading…
Reference in New Issue