First commit
This commit is contained in:
97
src/lib.rs
Normal file
97
src/lib.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
extern crate proc_macro;
|
||||
use paste::paste;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::parse::{Parse, ParseStream, Result};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{Expr, Ident, Token, parse_macro_input};
|
||||
|
||||
macro_rules! generate_headings {
|
||||
($($v:expr),*) => {
|
||||
$(
|
||||
paste! {
|
||||
#[proc_macro]
|
||||
pub fn [<heading$v>](item: TokenStream) -> TokenStream {
|
||||
heading(stringify!($v), item)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
generate_headings!(1, 2, 3, 4, 5, 6);
|
||||
|
||||
struct Property {
|
||||
name: Ident,
|
||||
value: Expr,
|
||||
}
|
||||
|
||||
impl Parse for Property {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name: Ident = input.parse()?;
|
||||
input.parse::<Token![=]>()?;
|
||||
let value: Expr = input.parse()?;
|
||||
Ok(Property { name, value })
|
||||
}
|
||||
}
|
||||
|
||||
struct Argument {
|
||||
value: Option<Ident>,
|
||||
}
|
||||
|
||||
impl Parse for Argument {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let value: Option<Ident> =
|
||||
if input.peek(Ident) && input.peek2(Token![:]) && !input.peek3(Token![:]) {
|
||||
let val = input.parse()?;
|
||||
input.parse::<Token![:]>()?;
|
||||
val
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Argument { value })
|
||||
}
|
||||
}
|
||||
|
||||
struct BaseElement {
|
||||
value: Expr,
|
||||
params: Punctuated<Property, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for BaseElement {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let value: Expr = input.parse()?;
|
||||
let _ = input.parse::<Token![,]>();
|
||||
let params: Punctuated<Property, Token![,]> = Punctuated::parse_terminated(input)?;
|
||||
Ok(BaseElement { value, params })
|
||||
}
|
||||
}
|
||||
|
||||
fn heading(level: &str, item: TokenStream) -> TokenStream {
|
||||
let BaseElement { value, params } = parse_macro_input!(item as BaseElement);
|
||||
|
||||
let expanded = match params.is_empty() {
|
||||
false => {
|
||||
let mut values = Vec::new();
|
||||
|
||||
let formated_names = params
|
||||
.iter()
|
||||
.map(|x| format!("{}=\"{{}}\"", x.name))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ");
|
||||
|
||||
let format_string = format!("<h{} {formated_names}>{{}}</h{}>", level, level);
|
||||
|
||||
for property in params {
|
||||
values.push(property.value);
|
||||
}
|
||||
|
||||
quote!(format!(#format_string, #(#values), *, #value))
|
||||
}
|
||||
true => {
|
||||
let format_string = format!("<h{}>{{}}</h{}>", level, level);
|
||||
quote!(format!(#format_string, #value))
|
||||
}
|
||||
};
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
Reference in New Issue
Block a user