blob: 25c253d82b674613ff8bc641f54ce9ef15fdde42 [file] [log] [blame]
#![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<Ident>,
/// The method to delegate the methods to.
#[darling(default)]
method: Option<Ident>,
}
#[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<T>` 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<bool>,
/// Whether to generate setters for this struct's public fields by default.
#[darling(default)]
generate_public: Option<bool>,
/// Whether to generate setters for this struct's private fields by default.
#[darling(default)]
generate_private: Option<bool>,
/// A prefix for the generated setter methods.
#[darling(default)]
prefix: Option<String>,
/// Other types to generate delegates to this type for. Note that this does not support
/// generics.
#[darling(multiple)]
generate_delegates: Vec<ExternalDelegate>,
}
#[derive(Debug, Clone, FromField)]
#[darling(attributes(setters), forward_attrs(doc))]
struct FieldAttrs {
attrs: Vec<Attribute>,
/// The name of the generated setter method.
#[darling(default)]
rename: Option<Ident>,
/// Whether to accept any field that converts via `Into` to this field's type.
#[darling(default)]
into: Option<bool>,
/// Whether to strip `Option<T>` into `T` in the parameters for the setter method.
#[darling(default)]
strip_option: Option<bool>,
/// Whether to borrow or take ownership of `self` for the setter method.
#[darling(default)]
borrow_self: Option<bool>,
/// Whether to generate a method that sets a boolean.
#[darling(default)]
bool: Option<bool>,
/// 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<ExternalDelegate>,
}
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<ContainerDef, SynTokenStream> {
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<Option<FieldDef>, 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<SynTokenStream>,
) -> Result<SynTokenStream, SynTokenStream> {
let FieldDef {
field_name, mut field_ty, field_doc, setter_name, ..
} = def;
let std = &container.std;
// Strips `Option<T>` 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<SynTokenStream>,
) -> Result<SynTokenStream, SynTokenStream> {
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<TokenStream, TokenStream> {
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()
}
}