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"
|
||||
version = "0.1.0"
|
||||
authors = ["Bodil Stokke <bodil@bodil.org>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "0.6.8"
|
||||
pom = "2.0.1"
|
||||
proc-macro2 = "0.4.20"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use std::collections::HashMap;
|
||||
use proc_macro::{Ident, Span, TokenStream};
|
||||
|
||||
use map::StringyMap;
|
||||
|
||||
pub fn required_children(element: &str) -> &[&str] {
|
||||
match element {
|
||||
|
@ -9,10 +10,13 @@ pub fn required_children(element: &str) -> &[&str] {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn global_attrs(span: Span) -> HashMap<Ident, TokenStream> {
|
||||
let mut attrs = HashMap::new();
|
||||
let mut insert = |key, value: &str| attrs.insert(Ident::new(key, span), value.parse().unwrap());
|
||||
insert("id", "crate::elements::CssId");
|
||||
insert("class", "crate::elements::CssClass");
|
||||
pub fn global_attrs(span: Span) -> StringyMap<Ident, TokenStream> {
|
||||
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");
|
||||
insert("class", "crate::elements::CssClass");
|
||||
}
|
||||
attrs
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
use pom::combinator::*;
|
||||
use pom::Parser;
|
||||
use proc_macro2::{Group, Ident, TokenStream, TokenTree};
|
||||
use quote::quote;
|
||||
use std::collections::HashMap;
|
||||
use proc_macro::{quote, Group, Ident, Literal, TokenStream, TokenTree};
|
||||
|
||||
use crate::config::global_attrs;
|
||||
use crate::parser::*;
|
||||
use config::global_attrs;
|
||||
use map::StringyMap;
|
||||
use parser::*;
|
||||
|
||||
// State
|
||||
|
||||
struct Declare {
|
||||
name: Ident,
|
||||
attrs: HashMap<Ident, TokenStream>,
|
||||
attrs: StringyMap<Ident, TokenStream>,
|
||||
req_children: Vec<Ident>,
|
||||
opt_children: Option<TokenStream>,
|
||||
traits: Vec<TokenStream>,
|
||||
|
@ -28,37 +27,33 @@ impl Declare {
|
|||
}
|
||||
}
|
||||
|
||||
fn elem_name(&self) -> Ident {
|
||||
fn elem_name(&self) -> TokenTree {
|
||||
Ident::new(
|
||||
&format!("Element_{}", self.name.to_string()),
|
||||
self.name.span(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn attr_names(&self) -> impl Iterator<Item = Ident> + '_ {
|
||||
self.attrs
|
||||
.keys()
|
||||
.map(|k| Ident::new(&format!("attr_{}", k.to_string()), k.span()))
|
||||
fn attrs(&self) -> impl Iterator<Item = (TokenTree, TokenStream, TokenTree)> + '_ {
|
||||
self.attrs.iter().map(|(key, value)| {
|
||||
let attr_name: TokenTree =
|
||||
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> + '_ {
|
||||
self.attrs.keys().map(|k| k.to_string())
|
||||
}
|
||||
|
||||
fn req_child_names(&self) -> impl Iterator<Item = Ident> + '_ {
|
||||
self.req_children
|
||||
.iter()
|
||||
.map(|c| Ident::new(&format!("child_{}", c.to_string()), c.span()))
|
||||
}
|
||||
|
||||
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 req_children(&self) -> impl Iterator<Item = (TokenTree, TokenTree, TokenTree)> + '_ {
|
||||
self.req_children.iter().map(|child| {
|
||||
let child_name: TokenTree =
|
||||
Ident::new(&format!("child_{}", child.to_string()), child.span()).into();
|
||||
let child_type: TokenTree =
|
||||
Ident::new(&format!("Element_{}", child.to_string()), child.span()).into();
|
||||
let child_str = Literal::string(&child.to_string()).into();
|
||||
(child_name, child_type, child_str)
|
||||
})
|
||||
}
|
||||
|
||||
fn into_token_stream(self) -> TokenStream {
|
||||
|
@ -74,46 +69,58 @@ impl Declare {
|
|||
|
||||
fn struct_(&self) -> TokenStream {
|
||||
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 {
|
||||
Some(child_constraint) => quote!(pub children: Vec<Box<#child_constraint>>),
|
||||
None => TokenStream::new(),
|
||||
};
|
||||
let mut body = 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!(
|
||||
pub struct #elem_name {
|
||||
#( pub #attr_name: Option<#attr_type>, )*
|
||||
pub data_attributes: std::collections::BTreeMap<String, String>,
|
||||
#( pub #req_child_name: Box<#req_child_type>, )*
|
||||
#children
|
||||
pub struct $elem_name {
|
||||
$body
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn impl_(&self) -> TokenStream {
|
||||
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 {
|
||||
Some(_) => quote!(children: Vec::new()),
|
||||
None => TokenStream::new(),
|
||||
};
|
||||
let mut args = TokenStream::new();
|
||||
for (child_name, child_type, _) in self.req_children() {
|
||||
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!(
|
||||
impl #elem_name {
|
||||
pub fn new(#(#req_child_name: Box<#req_child_type>),*) -> Self {
|
||||
#elem_name {
|
||||
#( #attr_name: None, )*
|
||||
data_attributes: std::collections::BTreeMap::new(),
|
||||
#( #req_child_name_again, )*
|
||||
#construct_children
|
||||
impl $elem_name {
|
||||
pub fn new($args) -> Self {
|
||||
$elem_name {
|
||||
$body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,43 +130,47 @@ impl Declare {
|
|||
fn impl_node(&self) -> TokenStream {
|
||||
let elem_name = self.elem_name();
|
||||
quote!(
|
||||
impl Node for #elem_name {}
|
||||
impl ::elements::Node for $elem_name {}
|
||||
)
|
||||
}
|
||||
|
||||
fn impl_element(&self) -> TokenStream {
|
||||
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!(
|
||||
impl Element for #elem_name {
|
||||
impl ::elements::Element for $elem_name {
|
||||
fn attributes() -> &'static [&'static str] {
|
||||
&[ #(#attr_name_str),* ]
|
||||
&[ $attrs ]
|
||||
}
|
||||
|
||||
fn required_children() -> &'static [&'static str] {
|
||||
&[ #(#req_child_str_name),* ]
|
||||
&[ $reqs ]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn impl_marker_traits(&self) -> TokenStream {
|
||||
let trait_for = std::iter::repeat(self.elem_name());
|
||||
let trait_name = self.traits.iter();
|
||||
quote!(
|
||||
#(
|
||||
impl #trait_name for #trait_for {}
|
||||
)*
|
||||
)
|
||||
let elem_name = self.elem_name();
|
||||
let mut body = TokenStream::new();
|
||||
for t in &self.traits {
|
||||
let name = t.clone();
|
||||
body.extend(quote!(
|
||||
impl $name for $elem_name {}
|
||||
));
|
||||
}
|
||||
body
|
||||
}
|
||||
|
||||
fn impl_display(&self) -> TokenStream {
|
||||
let elem_name = self.elem_name();
|
||||
let name = self.name.to_string();
|
||||
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 name: TokenTree = Literal::string(&self.name.to_string()).into();
|
||||
|
||||
let print_opt_children = if self.opt_children.is_some() {
|
||||
quote!(for child in &self.children {
|
||||
|
@ -168,14 +179,22 @@ impl Declare {
|
|||
} else {
|
||||
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() {
|
||||
quote!(if self.children.is_empty() {
|
||||
write!(f, "/>")
|
||||
} else {
|
||||
write!(f, ">")?;
|
||||
#print_opt_children
|
||||
write!(f, "</{}>", #name)
|
||||
$print_opt_children
|
||||
write!(f, "</{}>", $name)
|
||||
})
|
||||
} else {
|
||||
quote!(write!(f, "/>"))
|
||||
|
@ -183,27 +202,30 @@ impl Declare {
|
|||
} else {
|
||||
quote!(
|
||||
write!(f, ">")?;
|
||||
#(
|
||||
self.#req_child_name.fmt(f)?;
|
||||
)*
|
||||
#print_opt_children
|
||||
write!(f, "</{}>", #name)
|
||||
$print_req_children
|
||||
$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!(
|
||||
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> {
|
||||
write!(f, "<{}", #name);
|
||||
#(
|
||||
if let Some(ref value) = self.#attr_name {
|
||||
write!(f, " {}={:?}", #attr_name_str, value.to_string())?;
|
||||
}
|
||||
)*
|
||||
write!(f, "<{}", $name);
|
||||
$print_attrs
|
||||
for (key, value) in &self.data_attributes {
|
||||
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)>>>
|
||||
{
|
||||
group().map(|group: Group| {
|
||||
group().map(|group: Group| -> Vec<(Ident, TokenStream)> {
|
||||
let attr = ident() - punct(':') + type_spec();
|
||||
let parser = attr.repeat(0..);
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use pom::combinator::*;
|
||||
use pom::Parser;
|
||||
use proc_macro2::{Group, Ident, Literal, TokenStream, TokenTree};
|
||||
use quote::quote;
|
||||
use std::collections::BTreeMap;
|
||||
use proc_macro::{quote, Group, Ident, Literal, TokenStream, TokenTree};
|
||||
|
||||
use crate::config::required_children;
|
||||
use crate::parser::*;
|
||||
use config::required_children;
|
||||
use map::StringyMap;
|
||||
use parser::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Node {
|
||||
|
@ -19,7 +18,8 @@ impl Node {
|
|||
match self {
|
||||
Node::Element(el) => el.into_token_stream(),
|
||||
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"),
|
||||
}
|
||||
|
@ -30,20 +30,23 @@ impl Node {
|
|||
Node::Element(el) => {
|
||||
let el = el.into_token_stream();
|
||||
quote!(
|
||||
element.children.push(#el);
|
||||
element.children.push($el);
|
||||
)
|
||||
}
|
||||
tx @ Node::Text(_) => {
|
||||
let tx = tx.into_token_stream();
|
||||
quote!(
|
||||
element.children.push(#tx);
|
||||
element.children.push($tx);
|
||||
)
|
||||
}
|
||||
Node::Block(group) => quote!(
|
||||
for child in #group.into_iter() {
|
||||
Node::Block(group) => {
|
||||
let group: TokenTree = group.into();
|
||||
quote!(
|
||||
for child in $group.into_iter() {
|
||||
element.children.push(child);
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,12 +54,12 @@ impl Node {
|
|||
#[derive(Clone)]
|
||||
struct Element {
|
||||
name: Ident,
|
||||
attributes: BTreeMap<Ident, TokenTree>,
|
||||
attributes: StringyMap<Ident, TokenTree>,
|
||||
children: Vec<Node>,
|
||||
}
|
||||
|
||||
fn extract_data_attrs(attrs: &mut BTreeMap<Ident, TokenTree>) -> BTreeMap<String, TokenTree> {
|
||||
let mut data = BTreeMap::new();
|
||||
fn extract_data_attrs(attrs: &mut StringyMap<Ident, TokenTree>) -> StringyMap<String, TokenTree> {
|
||||
let mut data = StringyMap::new();
|
||||
let keys: Vec<Ident> = attrs.keys().cloned().collect();
|
||||
for key in keys {
|
||||
let key_name = key.to_string();
|
||||
|
@ -73,7 +76,7 @@ impl Element {
|
|||
fn new(name: Ident) -> Self {
|
||||
Element {
|
||||
name,
|
||||
attributes: BTreeMap::new(),
|
||||
attributes: StringyMap::new(),
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +84,7 @@ impl Element {
|
|||
fn into_token_stream(mut self) -> TokenStream {
|
||||
let name = self.name;
|
||||
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);
|
||||
if req_names.len() > self.children.len() {
|
||||
panic!(
|
||||
|
@ -92,34 +95,44 @@ impl Element {
|
|||
);
|
||||
}
|
||||
let data_attrs = extract_data_attrs(&mut self.attributes);
|
||||
let data_keys = data_attrs.keys().cloned();
|
||||
let data_values = data_attrs.values().cloned();
|
||||
let keys: Vec<_> = self
|
||||
.attributes
|
||||
.keys()
|
||||
.map(|key| Ident::new(&format!("attr_{}", key), key.span()))
|
||||
.collect();
|
||||
let values: Vec<TokenTree> = self.attributes.values().cloned().collect();
|
||||
let attrs = self.attributes.iter().map(|(key, value)| {
|
||||
(
|
||||
TokenTree::Ident(Ident::new(&format!("attr_{}", key), key.span())),
|
||||
value.clone(),
|
||||
)
|
||||
});
|
||||
let opt_children = self
|
||||
.children
|
||||
.split_off(req_names.len())
|
||||
.into_iter()
|
||||
.map(Node::into_child_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!(
|
||||
{
|
||||
let mut element = typed_html::elements::#typename::new(
|
||||
#({ #req_children }),*
|
||||
);
|
||||
#(
|
||||
element.#keys = Some(#values.into());
|
||||
)*
|
||||
#(
|
||||
element.data_attributes.insert(#data_keys.into(), #data_values.into());
|
||||
)*
|
||||
#(
|
||||
#opt_children
|
||||
)*
|
||||
let mut element = typed_html::elements::$typename::new($args);
|
||||
$body
|
||||
Box::new(element)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
#![feature(proc_macro_hygiene)]
|
||||
#![feature(proc_macro_quote)]
|
||||
#![feature(proc_macro_span)]
|
||||
|
||||
extern crate pom;
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro2::{TokenStream, TokenTree};
|
||||
use proc_macro::{TokenStream, TokenTree};
|
||||
|
||||
mod config;
|
||||
mod declare;
|
||||
mod html;
|
||||
mod map;
|
||||
mod parser;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: TokenStream = input.into();
|
||||
pub fn html(input: TokenStream) -> TokenStream {
|
||||
let input: Vec<TokenTree> = input.into_iter().collect();
|
||||
let result = html::expand_html(&input);
|
||||
match result {
|
||||
Err(error) => panic!(parser::parse_error(&input, &error)),
|
||||
Ok(ts) => ts.into(),
|
||||
Ok(ts) => ts,
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn declare_element(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: TokenStream = input.into();
|
||||
pub fn declare_element(input: TokenStream) -> TokenStream {
|
||||
let input: Vec<TokenTree> = input.into_iter().collect();
|
||||
let result = declare::expand_declare(&input);
|
||||
match result {
|
||||
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::{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>> {
|
||||
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 = ()>> {
|
||||
comb(move |input: &[TokenTree], start| match input.get(start) {
|
||||
Some(TokenTree::Ident(i)) => {
|
||||
if *i == name {
|
||||
if i.to_string() == name {
|
||||
Ok(((), start + 1))
|
||||
} else {
|
||||
Err(Error::Mismatch {
|
||||
|
@ -93,8 +93,7 @@ pub fn html_ident<'a>() -> Combinator<impl Parser<'a, TokenTree, Output = Ident>
|
|||
(
|
||||
match span {
|
||||
None => Some(token.span()),
|
||||
// FIXME: Some(span) => Some(span.join(token.span())),
|
||||
span => span,
|
||||
Some(span) => span.join(token.span()),
|
||||
},
|
||||
match token {
|
||||
TokenTree::Ident(ident) => name + &ident.to_string(),
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
name = "typed-html"
|
||||
version = "0.1.0"
|
||||
authors = ["Bodil Stokke <bodil@bodil.org>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
typed-html-macros = { path = "../macros" }
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate typed_html;
|
||||
extern crate typed_html_macros;
|
||||
|
||||
use typed_html_macros::html;
|
||||
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
extern crate http;
|
||||
extern crate typed_html_macros;
|
||||
|
||||
pub mod elements;
|
||||
|
|
Loading…
Reference in New Issue