mod convert;
mod forward;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
Result,
parse::{Parse, ParseStream},
parse_macro_input,
spanned::Spanned,
};
#[derive(Default)]
struct Configure {
inherent: bool,
from: bool,
try_into: bool,
inline: bool,
}
impl Parse for Configure {
fn parse(input: ParseStream) -> Result<Self> {
let mut inherent = false;
let mut from = false;
let mut try_into = false;
let mut inline = false;
while !input.is_empty() {
let ident: syn::Ident = input.parse()?;
match ident.to_string().as_str() {
"inherent" => inherent = true,
"from" => from = true,
"try_into" => try_into = true,
"inline" => inline = true,
_ => {
return Err(syn::Error::new(
ident.span(),
"Unknown configuration option",
));
}
}
if input.peek(syn::Token![,]) {
input.parse::<syn::Token![,]>()?;
}
}
Ok(Configure {
inherent,
from,
try_into,
inline,
})
}
}
struct Disponent(TokenStream);
impl Parse for Disponent {
fn parse(input: ParseStream) -> Result<Self> {
let input = input.parse::<TokenStream>()?;
let items = match syn::parse2::<syn::File>(input.clone()) {
Ok(f) => f.items,
Err(_) => return Ok(Disponent(input)),
};
let trait_def = items
.iter()
.find_map(|item| match item {
syn::Item::Trait(t) => Some(t.clone()),
_ => None,
})
.ok_or_else(|| syn::Error::new(input.span(), "Missing trait definition"))?;
let enum_def = items
.iter()
.find_map(|item| match item {
syn::Item::Enum(e) => Some(e.clone()),
_ => None,
})
.ok_or_else(|| syn::Error::new(input.span(), "Missing enum definition"))?;
let config = enum_def
.attrs
.iter()
.find(|attr| {
attr.path()
.segments
.last()
.is_some_and(|segment| segment.ident == "configure")
})
.map(|attr| attr.parse_args::<Configure>())
.transpose()?
.unwrap_or_default();
let forward_to_variant =
forward::forward_to_variant(config.inherent, config.inline, &enum_def, &trait_def)?;
let from_impl = if config.from {
convert::impl_from(&enum_def)?
} else {
TokenStream::new()
};
let try_into_impl = if config.try_into {
convert::impl_try_into(&enum_def)?
} else {
TokenStream::new()
};
let definition = quote::quote! {
#input
#forward_to_variant
#from_impl
#try_into_impl
};
Ok(Disponent(definition))
}
}
impl ToTokens for Disponent {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens)
}
}
#[proc_macro]
pub fn declare(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let disponent = parse_macro_input!(input as Disponent);
disponent.into_token_stream().into()
}
#[proc_macro_attribute]
pub fn configure(
_input: proc_macro::TokenStream,
out: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
out
}