use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, Visibility};
use crate::derive::visibility_suffix;
struct Bucket {
vis: Visibility,
marker: Ident,
witness: Ident,
}
pub(crate) struct SealBuilder {
prefix: String,
span: Span,
store_ty: TokenStream,
seal_generics: TokenStream,
seal_where: TokenStream,
trait_visibility: Visibility,
trait_name: Ident,
trait_generics_decl: TokenStream,
trait_generics_use: TokenStream,
trait_where: TokenStream,
buckets: Vec<Bucket>,
items: Vec<(TokenStream, TokenStream)>,
}
impl SealBuilder {
pub(crate) fn new(
prefix: String,
span: Span,
store_ty: TokenStream,
trait_name: Ident,
) -> Self {
Self {
prefix,
span,
store_ty,
seal_generics: TokenStream::new(),
seal_where: TokenStream::new(),
trait_visibility: Visibility::Inherited,
trait_name,
trait_generics_decl: TokenStream::new(),
trait_generics_use: TokenStream::new(),
trait_where: TokenStream::new(),
buckets: Vec::new(),
items: Vec::new(),
}
}
pub(crate) fn seal_generics(mut self, generics: TokenStream, where_: TokenStream) -> Self {
self.seal_generics = generics;
self.seal_where = where_;
self
}
pub(crate) fn trait_generics(
mut self,
decl: TokenStream,
use_: TokenStream,
where_: TokenStream,
) -> Self {
self.trait_generics_decl = decl;
self.trait_generics_use = use_;
self.trait_where = where_;
self
}
pub(crate) fn trait_visibility(mut self, vis: Visibility) -> Self {
self.trait_visibility = vis;
self
}
pub(crate) fn push_witness(&mut self, vis: &Visibility) -> Ident {
if let Some(b) = self.buckets.iter().find(|b| &b.vis == vis) {
return b.witness.clone();
}
let suffix = visibility_suffix(vis);
let marker = Ident::new(&format!("__{}Marker{}", self.prefix, suffix), self.span);
let witness = Ident::new(&format!("__{}VisibleIn{}", self.prefix, suffix), self.span);
self.buckets.push(Bucket {
vis: vis.clone(),
marker,
witness: witness.clone(),
});
witness
}
pub(crate) fn push_method(&mut self, sig: TokenStream, body: TokenStream) {
self.items.push((quote! { #sig; }, quote! { #sig #body }));
}
pub(crate) fn push_assoc(&mut self, trait_item: TokenStream, impl_item: TokenStream) {
self.items.push((trait_item, impl_item));
}
pub(crate) fn into_tokens(self) -> TokenStream {
let SealBuilder {
prefix,
span,
store_ty,
seal_generics,
seal_where,
trait_visibility,
trait_name,
trait_generics_decl,
trait_generics_use,
trait_where,
buckets,
items,
} = self;
let sealed = Ident::new(&format!("__{}Sealed", prefix), span);
let markers = buckets.iter().map(|b| {
let Bucket { vis, marker, .. } = b;
quote! {
#[doc(hidden)]
#[allow(non_camel_case_types)]
#vis struct #marker;
}
});
let witness_traits = buckets.iter().map(|b| {
let witness = &b.witness;
quote! {
#[doc(hidden)]
#[allow(non_camel_case_types)]
trait #witness<__V> {}
}
});
let witness_impls = buckets.iter().map(|b| {
let Bucket {
marker, witness, ..
} = b;
quote! {
impl #seal_generics #witness<#marker> for #store_ty #seal_where {}
}
});
let (trait_items, impl_items): (Vec<_>, Vec<_>) = items.into_iter().unzip();
quote! {
#(#markers)*
#(#witness_traits)*
#(#witness_impls)*
#[doc(hidden)]
#[allow(non_camel_case_types)]
trait #sealed {}
impl #seal_generics #sealed for #store_ty #seal_where {}
#[allow(private_bounds)]
#trait_visibility trait #trait_name #trait_generics_decl: #sealed #trait_where {
#(#trait_items)*
}
#[allow(private_bounds)]
impl #trait_generics_decl #trait_name #trait_generics_use for #store_ty #trait_where {
#(#impl_items)*
}
}
}
}