use proc_macro2::{Span, TokenStream};
use proc_macro_crate::{crate_name, FoundCrate};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::{Field, Generics, ItemEnum, ItemStruct, Visibility};
mod enumerator_naming;
#[derive(Clone)]
pub enum TokenStreamRefs<'a> {
None,
Single(&'a TokenStream),
Double(&'a TokenStream, &'a TokenStream),
Multiple(Vec<&'a TokenStream>),
}
impl<'a> TokenStreamRefs<'a> {
pub fn append_option(self, ts: Option<&'a TokenStream>) -> Self {
match ts {
Some(ts) => match self {
TokenStreamRefs::None => TokenStreamRefs::Single(ts),
TokenStreamRefs::Single(ts1) => TokenStreamRefs::Double(ts1, ts),
TokenStreamRefs::Double(ts1, ts2) => TokenStreamRefs::Multiple(vec![ts1, ts2, ts]),
TokenStreamRefs::Multiple(mut tss) => {
tss.push(ts);
TokenStreamRefs::Multiple(tss)
}
},
None => self,
}
}
}
impl<'a> From<&'a TokenStream> for TokenStreamRefs<'a> {
fn from(ts: &'a TokenStream) -> Self {
TokenStreamRefs::Single(ts)
}
}
impl<'a> From<Option<&'a TokenStream>> for TokenStreamRefs<'a> {
fn from(opt: Option<&'a TokenStream>) -> Self {
match opt {
Some(ts) => TokenStreamRefs::Single(ts),
None => TokenStreamRefs::None,
}
}
}
impl<'a> ToTokens for TokenStreamRefs<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
TokenStreamRefs::None => {}
TokenStreamRefs::Single(ts) => ts.to_tokens(tokens),
TokenStreamRefs::Double(ts1, ts2) => {
ts1.to_tokens(tokens);
ts2.to_tokens(tokens);
}
TokenStreamRefs::Multiple(tss) => {
for ts in tss {
ts.to_tokens(tokens);
}
}
}
}
}
#[derive(Clone)]
pub struct Target {
target_type_name: TokenStream,
target_type: Option<TokenStream>,
target_type_with_bound: Option<TokenStream>,
vis: Option<Visibility>,
enumerator_type_name: TokenStream,
enumerator_type: Option<TokenStream>,
enumerator_type_with_bound: Option<TokenStream>,
generic_params_simple: Option<TokenStream>,
generic_params_full: Option<TokenStream>,
where_clause: Option<TokenStream>,
enumerable_trait_path: TokenStream,
}
impl Target {
pub fn new_for_any(
target_type_name: impl Into<TokenStream>,
enumerator_type_name: impl Into<TokenStream>,
) -> Self {
let enumerable_trait_path = get_enumerable_trait_path().unwrap();
let target_type_name = target_type_name.into();
let enumerator_type_name = enumerator_type_name.into();
Self {
enumerable_trait_path,
enumerator_type_name,
target_type_name,
vis: None,
target_type: None,
target_type_with_bound: None,
enumerator_type: None,
enumerator_type_with_bound: None,
generic_params_simple: None,
generic_params_full: None,
where_clause: None,
}
}
pub fn new_for_struct(target: &ItemStruct) -> Result<Self, TokenStream> {
Self::new_for_any(
target.ident.to_token_stream(),
enumerator_naming::get_enumerator_name(&target.ident, &target.attrs)?.to_token_stream(),
)
.with_visibility(target.vis.clone())
.with_where_clause_from_generics_and_fields(&target.generics, target.fields.iter())
.with_generic_params_from_generics(&target.generics)
}
pub fn new_for_enum(target: &ItemEnum) -> Result<Self, TokenStream> {
Self::new_for_any(
target.ident.to_token_stream(),
enumerator_naming::get_enumerator_name(&target.ident, &target.attrs)?
.into_token_stream(),
)
.with_visibility(target.vis.clone())
.with_where_clause_from_generics_and_fields(
&target.generics,
target.variants.iter().flat_map(|v| v.fields.iter()),
)
.with_generic_params_from_generics(&target.generics)
}
}
#[allow(dead_code)]
impl Target {
pub fn with_visibility(mut self, vis: Visibility) -> Self {
self.vis = Some(vis);
self
}
pub fn with_target_type(
mut self,
target_type: impl Into<TokenStream>,
target_type_with_bound: impl Into<TokenStream>,
) -> Self {
self.target_type = Some(target_type.into());
self.target_type_with_bound = Some(target_type_with_bound.into());
self
}
pub fn with_enumerator_type(
mut self,
enumerator_type: impl Into<TokenStream>,
enumerator_type_with_bound: impl Into<TokenStream>,
) -> Self {
self.enumerator_type = Some(enumerator_type.into());
self.enumerator_type_with_bound = Some(enumerator_type_with_bound.into());
self
}
pub fn with_generic_params(
mut self,
generic_params_simple: impl Into<TokenStream>,
generic_params_full: impl Into<TokenStream>,
) -> Self {
self.generic_params_simple = Some(generic_params_simple.into());
self.generic_params_full = Some(generic_params_full.into());
self
}
pub fn with_generic_params_from_generics(
self,
generics: &Generics,
) -> Result<Self, TokenStream> {
if generics.params.is_empty() {
return self.into_ok();
}
if let Some(lifetime) = generics.lifetimes().next() {
return Err(
quote_spanned!(lifetime.lifetime.span() => compile_error!("Lifetime parameters are not supported.")),
);
}
if let Some(const_param) = generics.const_params().next() {
return Err(
quote_spanned!(const_param.ident.span() => compile_error!("Const parameters are not supported.")),
);
}
let mut params_simple = quote!(<);
let mut params_full = quote!(<);
for param in generics.type_params() {
let ident = ¶m.ident;
let colon_token = ¶m.colon_token;
let bounds = ¶m.bounds;
params_simple.extend(quote!(#ident,));
params_full.extend(quote!(#ident #colon_token #bounds,));
}
params_simple.extend(quote!(>));
params_full.extend(quote!(>));
self.with_generic_params(params_simple, params_full)
.into_ok()
}
pub fn with_where_clause(mut self, where_clause: impl Into<TokenStream>) -> Self {
self.where_clause = Some(where_clause.into());
self
}
pub fn with_where_clause_from_generics_and_fields<'a>(
self,
generics: &'a Generics,
fields: impl Iterator<Item = &'a Field>,
) -> Self {
let enumerable_trait_path = &self.enumerable_trait_path;
let mut where_clause_for_fields = TokenStream::new();
for field in fields {
let ty = &field.ty;
where_clause_for_fields.extend(quote!(#ty: #enumerable_trait_path,));
}
for param in generics.type_params() {
let ident = ¶m.ident;
where_clause_for_fields.extend(quote!(#ident: ::core::marker::Copy,));
}
let where_clause = match &generics.where_clause {
Some(wc) => {
let predicates = wc.predicates.iter();
quote!(where #where_clause_for_fields #(#predicates,)*)
}
None => quote!(where #where_clause_for_fields),
};
self.with_where_clause(where_clause)
}
pub fn into_ok<T>(self) -> Result<Self, T> {
Ok(self)
}
}
#[allow(dead_code)]
impl Target {
pub fn target_type_name(&self) -> TokenStreamRefs {
(&self.target_type_name).into()
}
pub fn target_type(&self) -> TokenStreamRefs {
self.target_type
.as_ref()
.map(Into::into)
.unwrap_or_else(|| {
self.target_type_name()
.append_option(self.generic_params_simple.as_ref())
})
}
pub fn target_type_bounded(&self) -> TokenStreamRefs {
self.target_type_with_bound
.as_ref()
.map(Into::into)
.unwrap_or_else(|| {
self.target_type_name()
.append_option(self.generic_params_full.as_ref())
})
}
pub fn visibility(&self) -> Visibility {
self.vis.clone().unwrap_or_else(|| {
Visibility::Public(syn::token::Pub {
span: Span::call_site(),
})
})
}
pub fn enumerator_type_name(&self) -> TokenStreamRefs {
(&self.enumerator_type_name).into()
}
pub fn enumerator_type(&self) -> TokenStreamRefs {
self.enumerator_type
.as_ref()
.map(Into::into)
.unwrap_or_else(|| {
self.enumerator_type_name()
.append_option(self.generic_params_simple.as_ref())
})
}
pub fn enumerator_type_bounded(&self) -> TokenStreamRefs {
self.enumerator_type_with_bound
.as_ref()
.map(Into::into)
.unwrap_or_else(|| {
self.enumerator_type_name()
.append_option(self.generic_params_full.as_ref())
})
}
pub fn generic_params_simple(&self) -> TokenStreamRefs {
self.generic_params_simple.as_ref().into()
}
pub fn generic_params_full(&self) -> TokenStreamRefs {
self.generic_params_full.as_ref().into()
}
pub fn where_clause(&self) -> TokenStreamRefs {
self.where_clause.as_ref().into()
}
pub fn enumerable_trait_path(&self) -> TokenStreamRefs {
(&self.enumerable_trait_path).into()
}
}
fn get_enumerable_trait_path() -> Result<TokenStream, TokenStream> {
match crate_name("enumerable") {
Ok(FoundCrate::Itself) => {
Ok(quote!(Enumerable))
}
Ok(FoundCrate::Name(name)) => {
let crate_name = format_ident!("{}", name);
Ok(quote!(::#crate_name::Enumerable))
}
Err(e) => {
let e = format!("failed to find crate `enumerable`: {}", e);
Err(quote!(compile_error!(#e);))
}
}
}