const_default_derive/
lib.rs1#![doc(html_root_url = "http://docs.rs/const-default-derive/0.2.0")]
2
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use proc_macro2::{Ident, Literal, Span, TokenStream as TokenStream2};
7use proc_macro_crate::{crate_name, FoundCrate};
8use quote::{quote, quote_spanned};
9use syn::{spanned::Spanned, Error};
10
11#[proc_macro_derive(ConstDefault, attributes(const_default))]
51pub fn derive(input: TokenStream) -> TokenStream {
52 match derive_default(input.into()) {
53 Ok(output) => output.into(),
54 Err(error) => error.to_compile_error().into(),
55 }
56}
57
58fn derive_default(input: TokenStream2) -> Result<TokenStream2, syn::Error> {
60 let crate_ident = query_crate_ident()?;
61 let input = syn::parse2::<syn::DeriveInput>(input)?;
62 let ident = input.ident;
63 let data_struct = match input.data {
64 syn::Data::Struct(data_struct) => data_struct,
65 _ => {
66 return Err(Error::new(
67 Span::call_site(),
68 "ConstDefault derive only works on struct types",
69 ))
70 }
71 };
72 let default_impl =
73 generate_default_impl_struct(&crate_ident, &data_struct)?;
74 let mut generics = input.generics;
75 generate_default_impl_where_bounds(
76 &crate_ident,
77 &data_struct,
78 &mut generics,
79 )?;
80 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
81 Ok(quote! {
82 impl #impl_generics #crate_ident::ConstDefault for #ident #ty_generics #where_clause {
83 const DEFAULT: Self = #default_impl;
84 }
85 })
86}
87
88fn query_crate_ident() -> Result<TokenStream2, syn::Error> {
94 let query = crate_name("const-default").map_err(|error| {
95 Error::new(
96 Span::call_site(),
97 format!(
98 "could not find root crate for ConstDefault derive: {}",
99 error
100 ),
101 )
102 })?;
103 match query {
104 FoundCrate::Itself => Ok(quote! { crate }),
105 FoundCrate::Name(name) => {
106 let ident = Ident::new(&name, Span::call_site());
107 Ok(quote! { ::#ident })
108 }
109 }
110}
111
112fn generate_default_impl_struct(
122 crate_ident: &TokenStream2,
123 data_struct: &syn::DataStruct,
124) -> Result<TokenStream2, syn::Error> {
125 let fields_impl =
126 data_struct.fields.iter().enumerate().map(|(n, field)| {
127 let field_span = field.span();
128 let field_type = &field.ty;
129 let field_pos = Literal::usize_unsuffixed(n);
130 let field_ident = field
131 .ident
132 .as_ref()
133 .map(|ident| quote_spanned!(field_span=> #ident))
134 .unwrap_or_else(|| quote_spanned!(field_span=> #field_pos));
135 quote_spanned!(field_span=>
136 #field_ident: <#field_type as #crate_ident::ConstDefault>::DEFAULT
137 )
138 });
139 Ok(quote! {
140 Self {
141 #( #fields_impl ),*
142 }
143 })
144}
145
146fn generate_default_impl_where_bounds(
148 crate_ident: &TokenStream2,
149 data_struct: &syn::DataStruct,
150 generics: &mut syn::Generics,
151) -> Result<(), syn::Error> {
152 let where_clause = generics.make_where_clause();
153 for field in &data_struct.fields {
154 let field_type = &field.ty;
155 where_clause.predicates.push(syn::parse_quote!(
156 #field_type: #crate_ident::ConstDefault
157 ))
158 }
159 Ok(())
160}