#![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic))] extern crate proc_macro; use darling::*; use darling::util::Flag; use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as SynTokenStream}; use std::result::Result; use syn::*; use syn::spanned::Spanned; use quote::*; #[cfg(feature = "nightly")] fn error(span: Span, data: &str) -> SynTokenStream { span.unstable().error(data).emit(); SynTokenStream::new() } #[cfg(not(feature = "nightly"))] fn error(_: Span, data: &str) -> SynTokenStream { quote! { compile_error!(#data); } } #[derive(Debug, Clone, FromMeta)] struct ExternalDelegate { /// The type to generate a delegate for. ty: Path, /// The field to delegate the methods to. #[darling(default)] field: Option, /// The method to delegate the methods to. #[darling(default)] method: Option, } #[derive(Debug, Clone, FromDeriveInput)] #[darling(attributes(setters), supports(struct_named))] struct ContainerAttrs { ident: Ident, generics: Generics, /// Use the core library rather than the std library. #[darling(default)] no_std: Flag, /// Whether to accept any field that converts via `Into` by default. #[darling(default)] into: bool, /// Whether to strip `Option` into `T` in the parameters for the setter method by default. #[darling(default)] strip_option: bool, /// Whether to borrow or take ownership of `self` in the setter method by default. #[darling(default)] borrow_self: bool, /// Whether to generate a method that sets a boolean by default. #[darling(default)] bool: bool, /// Whether to generate setters for this struct's fields by default. If set to false, /// `generate_public` and `generate_private` are ignored. #[darling(default)] generate: Option, /// Whether to generate setters for this struct's public fields by default. #[darling(default)] generate_public: Option, /// Whether to generate setters for this struct's private fields by default. #[darling(default)] generate_private: Option, /// A prefix for the generated setter methods. #[darling(default)] prefix: Option, /// Other types to generate delegates to this type for. Note that this does not support /// generics. #[darling(multiple)] generate_delegates: Vec, } #[derive(Debug, Clone, FromField)] #[darling(attributes(setters), forward_attrs(doc))] struct FieldAttrs { attrs: Vec, /// The name of the generated setter method. #[darling(default)] rename: Option, /// Whether to accept any field that converts via `Into` to this field's type. #[darling(default)] into: Option, /// Whether to strip `Option` into `T` in the parameters for the setter method. #[darling(default)] strip_option: Option, /// Whether to borrow or take ownership of `self` for the setter method. #[darling(default)] borrow_self: Option, /// Whether to generate a method that sets a boolean. #[darling(default)] bool: Option, /// Whether to generate a setter for this field regardless of global settings. #[darling(default)] generate: bool, /// Whether to skip this field regardless of global settings. Overwrites `generate`. #[darling(default)] skip: bool, } struct ContainerDef { name: Ident, ty: Type, std: Ident, generics: Generics, prefix: String, uses_into: bool, strip_option: bool, borrow_self: bool, bool: bool, generate_public: bool, generate_private: bool, generate_delegates: Vec, } struct FieldDef { field_name: Ident, field_ty: Type, field_doc: SynTokenStream, setter_name: Ident, uses_into: bool, strip_option: bool, borrow_self: bool, bool: bool, } fn init_container_def(input: &DeriveInput) -> Result { let darling_attrs: ContainerAttrs = match FromDeriveInput::from_derive_input(input) { Ok(v) => v, Err(e) => return Err(e.write_errors()), }; let ident = &darling_attrs.ident; let (_, generics_ty, _) = darling_attrs.generics.split_for_impl(); let ty = quote! { #ident #generics_ty }; let generate = darling_attrs.generate.unwrap_or(true); Ok(ContainerDef { name: darling_attrs.ident, ty: parse2(ty).expect("Internal error: failed to parse internally generated type."), std: if darling_attrs.no_std.is_present() { Ident::new("core", Span::call_site()) } else { Ident::new("std", Span::call_site()) }, borrow_self: darling_attrs.borrow_self, generics: darling_attrs.generics, prefix: darling_attrs.prefix.unwrap_or(String::new()), uses_into: darling_attrs.into, strip_option: darling_attrs.strip_option, bool: darling_attrs.bool, generate_public: generate && darling_attrs.generate_public.unwrap_or(true), generate_private: generate && darling_attrs.generate_private.unwrap_or(true), generate_delegates: darling_attrs.generate_delegates, }) } fn init_field_def( container: &ContainerDef, field: &Field, ) -> Result, SynTokenStream> { // Decode the various attribute options. let darling_attrs: FieldAttrs = match FromField::from_field(field) { Ok(v) => v, Err(e) => return Err(e.write_errors()), }; // Check whether this field should generate a setter. if darling_attrs.skip { return Ok(None) } let generates = match field.vis { Visibility::Public(_) => container.generate_public, _ => container.generate_private, }; if !(darling_attrs.generate || generates) { return Ok(None) } // Returns a definition for this field. let ident = match &field.ident { Some(i) => i.clone(), None => panic!("Internal error: init_field_def on wrong item."), }; Ok(Some(FieldDef { field_name: ident.clone(), field_ty: field.ty.clone(), field_doc: if let Visibility::Public(_) = field.vis { let doc_str = format!("Sets the [`{}`](#structfield.{}) field of this struct.", ident, ident); quote! { #[doc = #doc_str] } } else { let attrs = darling_attrs.attrs; quote! { #( #attrs )* } }, setter_name: darling_attrs.rename.unwrap_or_else(|| Ident::new(&format!("{}{}", container.prefix, ident), ident.span()) ), uses_into: darling_attrs.into.unwrap_or(container.uses_into), strip_option: darling_attrs.strip_option.unwrap_or(container.strip_option), borrow_self: darling_attrs.borrow_self.unwrap_or(container.borrow_self), bool: darling_attrs.bool.unwrap_or(container.bool), })) } fn generate_setter_method( container: &ContainerDef, def: FieldDef, delegate_toks: &Option, ) -> Result { let FieldDef { field_name, mut field_ty, field_doc, setter_name, .. } = def; let std = &container.std; // Strips `Option` into `T` if the `strip_option` property is set. let mut stripped_option = false; if def.strip_option { if let Type::Path(path) = &field_ty { let segment = path.path.segments.last().unwrap(); if segment.ident.to_string() == "Option" { if let PathArguments::AngleBracketed(path) = &segment.arguments { if let GenericArgument::Type(tp) = path.args.first().unwrap() { field_ty = tp.clone(); stripped_option = true; } } } } } // The type the setter accepts. let value_ty = if def.uses_into { quote! { impl ::#std::convert::Into<#field_ty> } } else { quote! { #field_ty } }; // The expression actually stored into the field. let mut expr = quote! { value }; if def.uses_into { expr = quote! { #expr.into() }; } if def.bool { expr = quote! { true }; } if stripped_option { expr = quote! { Some(#expr) }; } // Handle the parameters when bool is enabled. let params = if def.bool { SynTokenStream::new() } else { quote! { value: #value_ty } }; // Generates the setter method itself. let container_name = &container.name; if let Some(delegate) = delegate_toks { let _self = if def.borrow_self { quote! { &mut self } } else { quote! { mut self } }; let return_self = if def.borrow_self { quote! { &mut Self } } else { quote! { Self } }; Ok(quote! { #field_doc pub fn #setter_name (#_self, #params) -> #return_self { self.#delegate.#field_name = #expr; self } }) } else { if def.borrow_self { Ok(quote! { #field_doc pub fn #setter_name (&mut self, #params) -> &mut Self { self.#field_name = #expr; self } }) } else { Ok(quote! { #field_doc pub fn #setter_name (self, #params) -> Self { #container_name { #field_name: #expr, ..self } } }) } } } fn generate_setters_for( input: &DeriveInput, data: &DataStruct, generics: &Generics, ty: SynTokenStream, delegate_toks: Option, ) -> Result { let container_def = init_container_def(&input)?; let mut toks = SynTokenStream::new(); for field in &data.fields { if let Some(field_def) = init_field_def(&container_def, field)? { let method = generate_setter_method(&container_def, field_def, &delegate_toks)?; toks.extend(method); } } let (generics_bound, _, generics_where) = generics.split_for_impl(); Ok(quote! { impl #generics_bound #ty #generics_where { #toks } }) } fn generate_setters(input: &DeriveInput, data: &DataStruct) -> Result { let container_def = init_container_def(&input)?; let mut toks = SynTokenStream::new(); let container_ty = &container_def.ty; toks.extend(generate_setters_for( input, data, &container_def.generics, quote! { #container_ty }, None, )); for delegate in container_def.generate_delegates { let delegate_ty = delegate.ty; toks.extend(generate_setters_for( input, data, &Generics::default(), quote! { #delegate_ty }, if delegate.field.is_some() && delegate.method.is_some() { return Err(error(input.span(), "Cannot set both `method` and `field` on a delegate.").into()); } else if let Some(field) = &delegate.field { Some(quote! { #field }) } else if let Some(method) = &delegate.method { Some(quote! { #method() }) } else { return Err(error(input.span(), "Must set either `method` or `field` on a delegate.").into()); } )); } Ok(toks.into()) } #[proc_macro_derive(Setters, attributes(setters))] pub fn derive_setters(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); if let Data::Struct(data) = &input.data { match generate_setters(&input, data) { Ok(toks) => toks, Err(toks) => toks, } } else { error(input.span(), "`#[derive(Setters)] may only be used on structs.").into() } }