vendor dbg-pls

This commit is contained in:
nora 2022-04-23 23:19:49 +02:00
parent a633546c8f
commit 57ba4cef3c
23 changed files with 2240 additions and 419 deletions

View file

@ -0,0 +1,17 @@
[package]
name = "dbg-pls-derive"
version = "0.2.1"
authors = ["Conrad Ludgate <conradludgate@gmail.com>"]
edition = "2018"
description = "derive(Debug)"
license = "MIT"
repository = "https://github.com/conradludgate/dbg-pls"
readme = "../README.md"
[lib]
proc-macro = true
[dependencies]
quote = "1.0"
syn = "1.0"
proc-macro2 = "1.0"

View file

@ -0,0 +1,117 @@
use crate::pat::{named_idents, unnamed_idents, PatternImpl};
use proc_macro2::{Ident, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use syn::{spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput, Fields, Path, Variant};
pub struct DebugImpl<T>(pub T);
impl ToTokens for DebugImpl<(Path, DeriveInput)> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let (path, input) = &self.0;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let ident = &input.ident;
let body = DebugImpl((ident, &input.data));
tokens.extend(quote! {
impl #impl_generics #path::DebugPls for #ident #ty_generics #where_clause {
fn fmt(&self, f: #path::Formatter<'_>) {
#body
}
}
})
}
}
impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a Data)> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let (name, data) = self.0;
match data {
Data::Struct(s) => DebugImpl((name, s)).to_tokens(tokens),
Data::Enum(e) => DebugImpl((name, e)).to_tokens(tokens),
Data::Union(_) => tokens
.extend(syn::Error::new(self.span(), "unions not supported").into_compile_error()),
}
}
}
impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a DataStruct)> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let (name, data) = self.0;
let pat = PatternImpl(&data.fields);
tokens.extend(quote! {
let #name #pat = self;
});
DebugImpl((name, &data.fields)).to_tokens(tokens)
}
}
impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a DataEnum)> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let (name, data) = self.0;
let variants = data.variants.iter().map(|v| DebugImpl((name, v)));
tokens.extend(quote! {
match self {
#( #variants )*
}
});
}
}
impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a Variant)> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let (name, variant) = self.0;
let Variant {
attrs: _,
ident,
fields,
discriminant: _,
} = &variant;
let pattern = PatternImpl(fields);
let debug = DebugImpl((ident, fields));
tokens.extend(quote! {
#name::#ident #pattern => { #debug }
});
}
}
impl<'a> ToTokens for DebugImpl<(&'a Ident, &'a Fields)> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let (name, fields) = self.0;
let name = name.to_string();
match fields {
Fields::Named(named) => {
tokens.extend(quote! {
f.debug_struct(#name)
});
named_idents(named).for_each(|ident| {
let name = ident.to_string();
tokens.extend(quote! {
.field(#name, #ident)
})
});
tokens.extend(quote! {
.finish()
});
}
Fields::Unnamed(unnamed) => {
tokens.extend(quote! {
f.debug_tuple_struct(#name)
});
unnamed_idents(unnamed).for_each(|ident| {
tokens.extend(quote! {
.field(#ident)
})
});
tokens.extend(quote! {
.finish()
});
}
Fields::Unit => tokens.extend(quote! {
f.debug_ident(#name)
}),
}
}
}

View file

@ -0,0 +1,61 @@
use debug::DebugImpl;
use predicate::predicate;
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{parse_macro_input, DeriveInput, parse_quote, Attribute, Path};
mod debug;
mod pat;
mod predicate;
/// Derives the standard `DebugPls` implementation.
///
/// Works exactly like [`Debug`]
#[proc_macro_derive(DebugPls, attributes(dbg_pls))]
pub fn derive(input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as DeriveInput);
let path = match get_crate(&input.attrs) {
Ok(path) => path,
Err(err) => return err.into_compile_error().into_token_stream().into(),
};
predicate(&mut input, path.clone());
DebugImpl((path, input)).into_token_stream().into()
}
fn get_crate(attrs: &[Attribute]) -> syn::Result<Path> {
fn parse_crate(lit: syn::Lit) -> syn::Result<Path> {
match lit {
syn::Lit::Str(s) => syn::parse_str(&s.value()),
_ => Err(syn::Error::new(lit.span(), "invalid crate name")),
}
}
fn parse_meta(meta: syn::Meta) -> Option<syn::Result<Path>> {
if let syn::Meta::List(list) = meta {
for meta in list.nested {
if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = meta {
if let Some(ident) = nv.path.get_ident() {
if *ident == "crate" {
return Some(parse_crate(nv.lit));
}
}
}
}
}
None
}
for attr in attrs {
if let Some(ident) = attr.path.get_ident() {
if *ident == "dbg_pls" {
if let Some(path) = parse_meta(Attribute::parse_meta(attr)?).transpose()? {
return Ok(path);
}
}
}
}
Ok(parse_quote! { ::dbg_pls })
}

View file

@ -0,0 +1,46 @@
use proc_macro2::{Delimiter, Group, Ident, TokenStream as TokenStream2, TokenTree};
use quote::{format_ident, quote, ToTokens};
use syn::{spanned::Spanned, Fields, FieldsNamed, FieldsUnnamed};
pub struct PatternImpl<T>(pub T);
impl<'a> ToTokens for PatternImpl<&'a Fields> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match &self.0 {
Fields::Named(named) => PatternImpl(named).to_tokens(tokens),
Fields::Unnamed(unnamed) => PatternImpl(unnamed).to_tokens(tokens),
Fields::Unit => {}
}
}
}
impl<'a> ToTokens for PatternImpl<&'a FieldsNamed> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let idents = named_idents(self.0);
let inner = quote! { #(#idents),* };
tokens.extend([TokenTree::Group(Group::new(Delimiter::Brace, inner))])
}
}
impl<'a> ToTokens for PatternImpl<&'a FieldsUnnamed> {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let idents = unnamed_idents(self.0);
let inner = quote! { #(#idents),* };
tokens.extend([TokenTree::Group(Group::new(Delimiter::Parenthesis, inner))])
}
}
pub fn unnamed_idents(fields: &FieldsUnnamed) -> impl Iterator<Item = Ident> + '_ {
fields
.unnamed
.iter()
.enumerate()
.map(|(i, field)| format_ident!("val{}", i, span = field.span()))
}
pub fn named_idents(fields: &FieldsNamed) -> impl Iterator<Item = &Ident> + '_ {
fields
.named
.iter()
.map(|field| field.ident.as_ref().unwrap())
}

View file

@ -0,0 +1,57 @@
use quote::format_ident;
use syn::{
punctuated::Punctuated, token, Data, DeriveInput, Field, Fields, Path, PredicateType,
TraitBound, TypeParamBound, WhereClause, WherePredicate,
};
pub fn predicate(input: &mut DeriveInput, path: Path) {
let mut pred = Pred {
wc: input.generics.make_where_clause(),
path,
};
match &input.data {
Data::Struct(s) => pred.fields(&s.fields),
Data::Enum(e) => e.variants.iter().for_each(|var| pred.fields(&var.fields)),
Data::Union(_) => todo!(),
}
}
struct Pred<'a> {
wc: &'a mut WhereClause,
path: Path,
}
impl<'a> Pred<'a> {
fn dbg_pls(&mut self, field: &Field) {
let mut bounds = Punctuated::new();
let mut path = self.path.clone();
path.segments.push(syn::PathSegment {
ident: format_ident!("DebugPls"),
arguments: syn::PathArguments::None,
});
bounds.push(TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path,
}));
self.wc.predicates.push(WherePredicate::Type(PredicateType {
lifetimes: None,
bounded_ty: field.ty.clone(),
colon_token: token::Colon::default(),
bounds,
}))
}
fn fields(&mut self, fields: &Fields) {
match fields {
syn::Fields::Named(named) => named.named.iter().for_each(|f| self.dbg_pls(f)),
syn::Fields::Unnamed(unnamed) => unnamed.unnamed.iter().for_each(|f| self.dbg_pls(f)),
syn::Fields::Unit => {}
}
}
}