derive_newtype/
lib.rs

1//! Proc-macro implementation for `newtype`. You probably shouldn't use this
2//! crate directly.
3
4extern crate proc_macro;
5
6use proc_macro::TokenStream;
7
8use syn::{
9    Data,
10    Fields,
11};
12
13use quote::quote;
14
15/// Treat a single-field tuple struct as a "newtype"
16///
17/// This will implement `From`, `Into`, `Deref`, and `DerefMut` for the inner
18/// type.
19#[proc_macro_derive(NewType)]
20pub fn newtype(input: TokenStream) -> TokenStream {
21    let input = syn::parse::<syn::DeriveInput>(input).expect("syn parse derive input");
22
23    gen_impl(input).into()
24}
25
26fn gen_impl(input: syn::DeriveInput) -> proc_macro2::TokenStream {
27    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
28    let name = input.ident;
29
30    let st = match input.data {
31        Data::Struct(st) => st,
32        _ => panic!("NewType can only be derived for single-field tuple structs"),
33    };
34
35    let fields = match st.fields {
36        Fields::Unnamed(fields) => fields,
37        _ => panic!("NewType can only be derived for single-field tuple structs"),
38    };
39
40    if fields.unnamed.len() != 1 {
41        panic!("NewType can only be derived for single-field tuple structs")
42    }
43
44    let field_ty = fields.unnamed.into_iter().nth(0).unwrap().ty;
45
46    let from = quote! {
47        impl #impl_generics From<#field_ty> for #name #ty_generics #where_clause {
48            fn from(other: #field_ty) -> #name #ty_generics {
49                #name (other)
50            }
51        }
52    };
53
54    let deref = quote! {
55        impl #impl_generics ::core::ops::Deref for #name #ty_generics #where_clause {
56            type Target = #field_ty;
57
58            fn deref(&self) -> &Self::Target {
59                &self.0
60            }
61        }
62    };
63
64    let deref_mut = quote! {
65        impl #impl_generics ::core::ops::DerefMut for #name #ty_generics #where_clause {
66            fn deref_mut(&mut self) -> &mut Self::Target {
67                &mut self.0
68            }
69        }
70    };
71
72    let into_inner = quote! {
73        impl #impl_generics #name #ty_generics #where_clause {
74            /// Unwrap to the inner type
75            pub fn into_inner(self) -> #field_ty {
76                self.0
77            }
78        }
79    };
80
81    quote! {
82        #from #deref #deref_mut #into_inner
83    }
84}