mod expand;
use proc_macro2::Ident;
use proc_macro2::Punct;
use proc_macro2::Span;
use proc_macro2::TokenStream;
use proc_macro2::TokenTree;
use quote::quote;
use syn::parse_macro_input;
use syn::DeriveInput;
struct VariantFieldGroupBasic {
tys: Vec<syn::Path>,
}
impl syn::parse::Parse for VariantFieldGroupBasic {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut tys = Vec::new();
let ty: syn::Path = input.parse()?;
tys.push(ty.clone());
loop {
if !input.peek(syn::token::Comma) {
break;
}
let _p: Punct = input.parse()?;
let ty: syn::Path = input.parse()?;
tys.push(ty);
}
let ret = Self { tys };
Ok(ret)
}
}
struct VariantFieldGroupFrom {
tys: Vec<syn::Path>,
}
impl syn::parse::Parse for VariantFieldGroupFrom {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let _p: Punct = input.parse()?;
let _gr: proc_macro2::Group = input.parse()?;
let mut tys = Vec::new();
let ty: syn::Path = input.parse()?;
tys.push(ty.clone());
loop {
if !input.peek(syn::token::Comma) {
break;
}
let _p: Punct = input.parse()?;
let ty: syn::Path = input.parse()?;
tys.push(ty);
}
let ret = Self { tys };
Ok(ret)
}
}
enum VariantFieldGroup {
Plain,
Basic(VariantFieldGroupBasic),
From(VariantFieldGroupFrom),
}
impl syn::parse::Parse for VariantFieldGroup {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.peek(syn::token::Pound) {
let v = input.parse()?;
let ret = Self::From(v);
Ok(ret)
} else {
let v = input.parse()?;
let ret = Self::Basic(v);
Ok(ret)
}
}
}
struct ErrorVariant {
name: Ident,
field_group: VariantFieldGroup,
}
struct MacroInput {
errname: Ident,
variants: Vec<ErrorVariant>,
}
impl syn::parse::Parse for MacroInput {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut tokens = Vec::new();
let a: Ident = input.parse()?;
let b: Punct = input.parse()?;
if a != "name" {
panic!("expect name")
}
if b.as_char() != ':' {
panic!("expect colon")
}
let errname: syn::Ident = input.parse()?;
let _: Punct = input.parse()?;
let n: Ident = input.parse()?;
let p: Punct = input.parse()?;
if p.as_char() != ':' {
panic!("expect colon")
}
let mut variants = None;
if n == "variants" {
let gr: proc_macro2::Group = input.parse()?;
let p: Punct = input.parse()?;
if p.as_char() != ';' {
panic!("expect semicolon")
}
let mut vars = Vec::new();
let mut curr: Option<ErrorVariant> = None;
let mut finish_curr = |curr| {
vars.push(curr);
};
let stream = gr.stream();
for token in stream {
match token {
TokenTree::Group(x) => {
tokens.push(format!("GROUP[{}]", x));
let fg: VariantFieldGroup = syn::parse(x.stream().into())?;
let curr = curr.as_mut().unwrap();
curr.field_group = fg;
}
TokenTree::Ident(x) => {
tokens.push(format!("IDENT[{}]", x));
if let Some(_curr) = curr.as_mut() {
panic!("variant already in progress");
} else {
let v = ErrorVariant {
name: x,
field_group: VariantFieldGroup::Plain,
};
curr = Some(v);
}
}
TokenTree::Punct(x) => {
tokens.push(format!("PUNCT[{}]", x));
if let Some(curr) = curr.take() {
finish_curr(curr);
}
}
TokenTree::Literal(x) => {
tokens.push(format!("LITERAL[{}]", x));
}
}
}
if let Some(curr) = curr.take() {
finish_curr(curr);
}
variants = Some(vars);
}
let ret = Self {
errname,
variants: variants.expect("error variants"),
};
Ok(ret)
}
}
#[proc_macro]
pub fn create_error_v1(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as MacroInput);
let errname = input.errname.clone();
let errname_litstr = syn::LitStr::new(&input.errname.to_string(), Span::call_site());
let mut vars_ts = TokenStream::new();
let mut froms_ts = TokenStream::new();
let mut fmtbranches_ts = TokenStream::new();
for v in input.variants {
let vn = &v.name;
let vn_litstr = syn::LitStr::new(&vn.to_string(), Span::call_site());
match v.field_group {
VariantFieldGroup::Plain => {
let q = quote! {
#vn,
};
vars_ts.extend(q);
let q = quote! {
#errname::#vn => ::core::write!(fmt, "{}::{}", #errname_litstr, #vn_litstr),
};
fmtbranches_ts.extend(q);
}
VariantFieldGroup::Basic(x) => {
let tys = &x.tys;
let q = quote! {
#vn(#(#tys),*),
};
vars_ts.extend(q);
let dvs: Vec<_> = x
.tys
.iter()
.zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
.map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
.collect();
let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
let q = quote! {
#errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname_litstr, #vn_litstr, #(#dvs),*),
};
fmtbranches_ts.extend(q);
}
VariantFieldGroup::From(x) => {
let tys = &x.tys;
let q = quote! {
#vn(#(#tys),*),
};
vars_ts.extend(q);
let dvs: Vec<_> = x
.tys
.iter()
.zip(['a', 'b', 'c', 'd', 'e', 'f', 'g'].iter())
.map(|x| syn::Ident::new(&x.1.to_string(), Span::call_site()))
.collect();
let plcs: Vec<String> = x.tys.iter().map(|_| "{}".to_string()).collect();
let fmtstr = format!("{{}}::{{}}({})", plcs.join(", "));
let q = quote! {
#errname::#vn(#(#dvs),*) => ::core::write!(fmt, #fmtstr, #errname_litstr, #vn_litstr, #(#dvs),*),
};
fmtbranches_ts.extend(q);
let ty = &x.tys[0];
let q = quote! {
impl From<#ty> for #errname {
fn from(value: #ty) -> Self {
Self::#vn(value)
}
}
};
froms_ts.extend(q);
}
}
}
let ret = quote! {
#[derive(Debug)]
pub enum #errname {
#vars_ts
}
#froms_ts
impl ::core::fmt::Display for #errname {
fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
#fmtbranches_ts
}
}
}
impl ::core::error::Error for #errname {}
fn show() -> &'static str {
""
}
};
ret.into()
}
#[proc_macro_derive(Error, attributes(backtrace, error, from, source, cstm))]
pub fn derive_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let helpers = expand::DummyHelpers::new();
expand::derive(&input, &helpers).into()
}