deride 0.2.0

Some vaguely useful proc-macros
Documentation
#![feature(proc_macro_diagnostic)]

use std::collections::HashSet;

use proc_macro::{Diagnostic, Level, TokenStream};
use quote::quote;
use syn::{Attribute, Data, DeriveInput, Path, parse_macro_input, parse_quote};

#[proc_macro_derive(Without, attributes(without))]
pub fn derive_answer_fn(input: TokenStream) -> TokenStream {
  let input = parse_macro_input!(input as DeriveInput);

  let struc = match &input.data {
    Data::Struct(struc) => struc,
    _ => {
      Diagnostic::new(Level::Error, "Without only works on structs").emit();
      return TokenStream::new();
    },
  };

  let without_attr: Attribute = parse_quote!(#[without]);

  let withouts = input
    .attrs
    .iter()
    .filter(|attr| attr.path() == &parse_quote!(without))
    .map(|attr| {
      let mut name: Option<Path> = None;
      let mut derives: HashSet<Path> = HashSet::new();

      attr.parse_nested_meta(|meta| {
        let value = meta.value().unwrap().parse::<Path>().unwrap();

        if meta.path.is_ident("name") {
          name = Some(value);
        } else if meta.path.is_ident("derive") {
          derives.insert(value);
        }

        Ok(())
      }).unwrap();

      let name = name.unwrap();

      Without { name, derives }
    })
    .collect::<Vec<_>>();

  let mut expanded = Vec::new();

  #[allow(clippy::never_loop)]
  for Without { name, derives } in withouts {
    let new_fields = struc
      .fields
      .iter()
      .filter(|field| {
        let without = field
          .attrs
          .iter()
          .any(|attr| attribute(&without_attr, &name, attr));

        !without
      })
      .map(|field| {
        let mut field = field.clone();

        field.attrs.retain(|attr| attr.path() != without_attr.path());

        field
      })
      .collect::<Vec<_>>();

    let new_field_names = new_fields
      .iter()
      .map(|field| field.ident.clone())
      .collect::<Vec<_>>();

    let derives = if derives.is_empty() {
      quote! {}
    } else {
      let derives = derives.iter();
      quote! { #[derive(#(#derives),*)]}
    };

    let vis = input.vis.clone();
    let original_name = input.ident.clone();

    expanded.push(quote! {
      #derives
      #vis struct #name {
        #(#new_fields),*
      }

      impl From<#original_name> for #name {
        fn from(value: #original_name) -> #name {
          let #original_name { #(#new_field_names),*, .. } = value;

          #name {
            #(#new_field_names),*
          }
        }
      }
    });
  }

  let expanded = quote! {
    #(#expanded)*
  };

  TokenStream::from(expanded)
}

#[derive(Debug)]
struct Without {
  name: Path,
  derives: HashSet<Path>,
}

fn attribute(without_attr: &Attribute, name: &Path, attribute: &Attribute) -> bool {
  if attribute.path().get_ident() != without_attr.path().get_ident() {
    return false;
  }

  let mut matching_path = false;

  attribute
    .parse_nested_meta(|meta| {
      matching_path = &meta.path == name;

      Ok(())
    })
    .unwrap();

  matching_path
}