Crates.io Build Status Build Status Coverage Status

Proc Macro Roids

Traits and functions to make writing proc macros more ergonomic.

toml proc_macro_roids = "0.7.0"

Makes writing procedural macros much easier:

```rust extern crate proc_macro;

use procmacro::TokenStream; use procmacro2::Span; use procmacroroids::{DeriveInputStructExt, FieldExt, IdentExt}; use quote::quote; use syn::{parsemacroinput, parse_quote, DeriveInput, Ident};

/// Derives a Super enum with a variant for each struct field: /// /// rust,edition2018 /// use std::marker::PhantomData; /// use super_derive::Super; /// /// #[derive(Super)] /// pub struct Man<T> { /// #[super_derive(skip)] /// name: String, /// power_level: u64, /// marker: PhantomData<T>, /// } /// /// /// Generates: /// /// rust,ignore /// pub enum SuperMan { /// U64(u64), /// } ///

[procmacroderive(Super, attributes(super_derive))]

pub fn systemdescderive(input: TokenStream) -> TokenStream { let ast = parsemacroinput!(input as DeriveInput); let enumname = ast.ident.prepend("Super"); let fields = ast.fields(); let relevantfields = fields .iter() .filter(|field| !field.isphantomdata()) .filter(|field| !field.containstag(&parsequote!(superderive), &parsequote!(skip)));

let variants = relevant_fields
    .map(|field| {
        let type_name = field.type_name();
        let variant_name = type_name.to_string().to_uppercase();
        let variant_name = Ident::new(&variant_name, Span::call_site());
        quote! {
            #variant_name(#type_name)
        }
    })
    .collect::<Vec<_>>();

let token_stream2 = quote! {
    pub enum #enum_name {
        #(#variants,)*
    }
};

token_stream2.into()

} ```

Examples

1. Append additional #[derive(..)]s.

This works for function-like or attribute proc macros.

```rust extern crate proc_macro;

use procmacro::TokenStream; use procmacroroids::DeriveInputExt; use quote::quote; use syn::{parsemacroinput, parsequote, DeriveInput};

[procmacroattribute]

pub fn copy(args: TokenStream, item: TokenStream) -> TokenStream { // Example input: // // #[derive(Debug)] // struct Struct; let mut ast = parsemacro_input!(item as DeriveInput);

// Append the derives.
let derives = parse_quote!(Clone, Copy);
ast.append_derives(derives);

// Example output:
//
// #[derive(Debug, Clone, Copy)]
// struct Struct;
TokenStream::from(quote! { #ast })

} ```

2. Append named fields.

This works for structs with named fields or unit structs.

```rust extern crate proc_macro;

use procmacro::TokenStream; use procmacroroids::FieldsNamedAppend; use quote::quote; use syn::{parsemacroinput, parsequote, DeriveInput, FieldsNamed};

/// Example usage: /// /// rust /// use macro_crate::append_cd; /// /// #[append_cd] /// struct StructNamed { a: u32, b: i32 } ///

[procmacroattribute]

pub fn appendcd(args: TokenStream, item: TokenStream) -> TokenStream { // Example input: // // struct StructNamed { a: u32, b: i32 } let mut ast = parsemacroinput!(item as DeriveInput);

// Append the fields.
let fields_additional: FieldsNamed = parse_quote!({ c: i64, d: usize });
ast.append_named(fields_additional);

// Example output:
//
// struct StructNamed { a: u32, b: i32, c: i64, d: usize }
TokenStream::from(quote! { #ast })

} ```

3. Append unnamed fields (tuples).

This works for structs with unnamed fields or unit structs.

```rust extern crate proc_macro;

use procmacro::TokenStream; use procmacroroids::FieldsUnnamedAppend; use quote::quote; use syn::{parsemacroinput, parsequote, DeriveInput, FieldsUnnamed};

/// Example usage: /// /// rust /// use macro_crate::append_i64_usize; /// /// #[append_i64_usize] /// struct StructUnit; ///

[procmacroattribute]

pub fn appendi64usize(args: TokenStream, item: TokenStream) -> TokenStream { // Example input: // // struct StructUnit; let mut ast = parsemacro_input!(item as DeriveInput);

// Append the fields.
let fields_additional: FieldsUnnamed = parse_quote!((i64, usize));
ast.append_unnamed(fields_additional);

// Example output:
//
// struct StructUnit(i64, usize);
TokenStream::from(quote! { #ast })

} ```

4. Get newtype inner Field.

This works for structs with unnamed fields or unit structs.

```rust extern crate proc_macro;

use procmacro::TokenStream; use procmacroroids::DeriveInputNewtypeExt; use quote::quote; use syn::{parsemacroinput, parsequote, DeriveInput, Type};

[procmacroderive(Deref)]

pub fn derivederef(item: TokenStream) -> TokenStream { // Example input: // // #[derive(Deref)] // struct Newtype(u32); let mut ast = parsemacro_input!(item as DeriveInput);

// Get the inner field.
let inner_field = ast.inner_type();

// Implement `Deref`
let type_name = &ast.ident;
let token_stream_2 = quote! {
    #ast

    impl std::ops::Deref for #type_name {
        type Target = #inner_type;
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
}
TokenStream::from(token_stream_2)

} ```

5. Ident concatenation.

```rust,edition2018 use procmacroroids::IdentExt; use proc_macro2::Span; use syn::Ident;

fn main() {

let one = Ident::new("One", Span::callsite()); asserteq!(Ident::new("OneSuffix", Span::callsite()), one.append("Suffix")); asserteq!(Ident::new("PrefixOne", Span::call_site()), one.prepend("Prefix"));

let two = Ident::new("Two", Span::callsite()); asserteq!(Ident::new("OneTwo", Span::callsite()), one.append(&two)); asserteq!(Ident::new("TwoOne", Span::call_site()), one.prepend(&two));

}

```

6. Accessing struct fields.

```rust,edition2018 use procmacroroids::DeriveInputStructExt; use syn::{parse_quote, DeriveInput, Fields};

fn main() {

let ast: DeriveInput = parse_quote! { struct Named {} };

if let Fields::Named(..) = ast.fields() { // do something }

}

```

7. Inspecting Fields.

```rust,edition2018 use procmacroroids::FieldExt; use procmacro2::Span; use syn::{parsequote, Fields, FieldsNamed, Lit, LitStr, Meta, MetaNameValue, NestedMeta};

let fieldsnamed: FieldsNamed = parsequote! {{ #[my::derive(tag::name(param = "value"))] pub name: PhantomData, }}; let fields = Fields::from(fields_named); let field = fields.iter().next().expect("Expected field to exist.");

asserteq!(field.typename(), "PhantomData"); assert!(field.isphantomdata()); assert!(field.containstag(&parsequote!(my::derive), &parsequote!(tag::name))); asserteq!( field.tagparameter( &parsequote!(my::derive), &parsequote!(tag::name), ).expect("Expected parameter to exist."), NestedMeta::Meta(Meta::NameValue(MetaNameValue { path: parsequote!(param), eqtoken: Default::default(), lit: Lit::Str(LitStr::new("value", Span::callsite())), })), ); ```

8. (De)constructing Fields.

```rust,edition2018

use std::str::FromStr;

# use procmacroroids::{DeriveInputStructExt, FieldsExt};

use proc_macro2::{Span, TokenStream};

use syn::{parse_quote, DeriveInput};

use quote::quote;

# // Need to generate code that instantiates MyEnum::Struct: // enum MyEnum { // Struct { // field0: u32, // field1: u32, // } // }

let ast: DeriveInput = parsequote! { struct Struct { field0: u32, field1: u32, } }; let fields = ast.fields(); let constructionform = fields.constructionform(); let tokens = quote! { MyEnum::Struct #constructionform };

let expected = TokenStream::fromstr("MyEnum::Struct { field0, field1, }").unwrap(); asserteq!(expected.tostring(), tokens.tostring()); ```


Note: The roids name is chosen because, although these functions make it easy to perform certain operations, they may not always be good ideas =D!

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.