use proc_macro2::Span;
use quote::ToTokens;
use syn::{
Error, Ident, Path, PathArguments, PathSegment, PredicateType, Result, TraitBound,
TraitBoundModifier, Type, TypeParam, TypeParamBound, TypePath,
punctuated::Punctuated,
token::{Colon, Plus, Question},
visit::{Visit, visit_ident},
};
pub struct Checker {
error: Option<Error>,
}
impl Checker {
pub const fn new() -> Self {
Self { error: None }
}
pub fn error<T: ToTokens>(&mut self, tokens: T) {
let error = Error::new_spanned(tokens, MESSAGE);
if let Some(ref mut combined) = self.error {
combined.combine(error);
} else {
self.error = Some(error);
}
}
pub fn check_error(self) -> Option<Error> {
self.error
}
pub fn check(self) -> Result<()> {
self.check_error().map_or(Ok(()), Err)
}
}
impl<'a> Visit<'a> for Checker {
fn visit_ident(&mut self, identifier: &'a Ident) {
if identifier == SPECIAL {
self.error(identifier);
}
visit_ident(self, identifier);
}
}
pub const SIZED: &str = stringify!(Sized);
pub const SPECIAL: &str = stringify!(__T);
pub const MESSAGE: &str = "identifier `__T` is reserved for blanket implementations";
pub fn identifier() -> Ident {
Ident::new(SPECIAL, Span::call_site())
}
pub fn sized_identifier() -> Ident {
Ident::new(SIZED, Span::call_site())
}
pub fn path_for(identifier: Ident) -> Path {
let mut segments = Punctuated::new();
segments.push(PathSegment {
ident: identifier,
arguments: PathArguments::None,
});
Path {
leading_colon: None,
segments,
}
}
pub fn path() -> Path {
path_for(identifier())
}
pub fn sized_path() -> Path {
path_for(sized_identifier())
}
pub fn type_path() -> TypePath {
TypePath {
qself: None,
path: path(),
}
}
pub type Bounds = Punctuated<TypeParamBound, Plus>;
pub fn type_parameter() -> TypeParam {
TypeParam {
attrs: Vec::new(),
ident: identifier(),
colon_token: None,
bounds: Punctuated::new(),
eq_token: None,
default: None,
}
}
pub fn maybe_sized_bound() -> TraitBound {
TraitBound {
paren_token: None,
modifier: TraitBoundModifier::Maybe(Question::default()),
lifetimes: None,
path: sized_path(),
}
}
pub fn predicate_type(mut bounds: Bounds) -> PredicateType {
bounds.push(TypeParamBound::Trait(maybe_sized_bound()));
PredicateType {
lifetimes: None,
bounded_ty: Type::Path(type_path()),
colon_token: Colon::default(),
bounds,
}
}