use std::convert::TryFrom;
use std::{borrow::Cow, vec::IntoIter};
use crate::BuildMethod;
use darling::util::{Flag, PathList, SpannedValue};
use darling::{Error, FromMeta};
use proc_macro2::Span;
use syn::{spanned::Spanned, Attribute, Generics, Ident, Meta, Path};
use crate::{
BlockContents, Builder, BuilderField, BuilderFieldType, BuilderPattern, DefaultExpression,
Each, FieldConversion, Initializer, Setter,
};
#[derive(Debug, Clone)]
enum VisibilityAttr {
Public(Span),
Private,
Explicit(syn::Visibility),
None,
}
impl VisibilityAttr {
pub fn to_explicit_visibility(&self) -> Option<Cow<syn::Visibility>> {
match self {
Self::Public(span) => Some(Cow::Owned(syn::Visibility::Public(
parse_quote_spanned!(*span=> pub),
))),
Self::Private => Some(Cow::Owned(syn::Visibility::Inherited)),
Self::Explicit(v) => Some(Cow::Borrowed(v)),
Self::None => None,
}
}
}
impl Default for VisibilityAttr {
fn default() -> Self {
Self::None
}
}
impl FromMeta for VisibilityAttr {
fn from_list(items: &[darling::ast::NestedMeta]) -> darling::Result<Self> {
#[derive(FromMeta)]
struct VisibilityAttrInternal {
public: Flag,
private: Flag,
vis: Option<syn::Visibility>,
}
let VisibilityAttrInternal {
public,
private,
vis: explicit,
} = VisibilityAttrInternal::from_list(items)?;
let mut conflicts = Error::accumulator();
if public.is_present() {
if private.is_present() {
conflicts.push(
Error::custom("`public` and `private` cannot be used together")
.with_span(&private.span()),
);
}
if let Some(vis) = explicit {
conflicts.push(
Error::custom("`public` and `vis` cannot be used together").with_span(&vis),
);
}
conflicts.finish_with(Self::Public(public.span()))
} else if let Some(vis) = explicit {
if private.is_present() {
conflicts.push(Error::custom("`vis` and `private` cannot be used together"));
}
conflicts.finish_with(Self::Explicit(vis))
} else if private.is_present() {
conflicts.finish_with(Self::Private)
} else {
conflicts.finish_with(Self::None)
}
}
}
#[derive(Debug, Clone, FromMeta)]
struct BuildFnErrorGenerated {
validation_error: SpannedValue<bool>,
}
#[derive(Debug, Clone)]
enum BuildFnError {
Existing(Path),
Generated(BuildFnErrorGenerated),
}
impl BuildFnError {
fn as_existing(&self) -> Option<&Path> {
match self {
BuildFnError::Existing(p) => Some(p),
BuildFnError::Generated(_) => None,
}
}
fn as_generated(&self) -> Option<&BuildFnErrorGenerated> {
match self {
BuildFnError::Generated(e) => Some(e),
BuildFnError::Existing(_) => None,
}
}
}
impl FromMeta for BuildFnError {
fn from_meta(item: &Meta) -> darling::Result<Self> {
match item {
Meta::Path(_) => Err(Error::unsupported_format("word").with_span(item)),
Meta::List(_) => BuildFnErrorGenerated::from_meta(item).map(Self::Generated),
Meta::NameValue(i) => Path::from_expr(&i.value).map(Self::Existing),
}
}
}
#[derive(Debug, Clone, FromMeta)]
#[darling(default, and_then = Self::validation_needs_error)]
pub struct BuildFn {
skip: bool,
name: Ident,
validate: Option<Path>,
#[darling(flatten)]
visibility: VisibilityAttr,
error: Option<BuildFnError>,
}
impl BuildFn {
fn validation_needs_error(self) -> darling::Result<Self> {
let mut acc = Error::accumulator();
if self.validate.is_some() {
if let Some(BuildFnError::Generated(e)) = &self.error {
if !*e.validation_error {
acc.push(
Error::custom(
"Cannot set `error(validation_error = false)` when using `validate`",
)
.with_span(&e.validation_error.span()),
)
}
}
}
acc.finish_with(self)
}
}
impl Default for BuildFn {
fn default() -> Self {
BuildFn {
skip: false,
name: Ident::new("build", Span::call_site()),
validate: None,
visibility: Default::default(),
error: None,
}
}
}
#[derive(Debug, Clone, Default, FromMeta)]
pub struct FieldLevelFieldMeta {
#[darling(flatten)]
visibility: VisibilityAttr,
#[darling(rename = "ty")]
builder_type: Option<syn::Type>,
build: Option<BlockContents>,
}
#[derive(Debug, Clone, Default, FromMeta)]
pub struct StructLevelSetter {
prefix: Option<Ident>,
into: Option<bool>,
strip_option: Option<bool>,
skip: Option<bool>,
}
impl StructLevelSetter {
pub fn enabled(&self) -> Option<bool> {
self.skip.map(|x| !x)
}
}
fn parse_each(meta: &Meta) -> darling::Result<Option<Each>> {
if let Meta::NameValue(mnv) = meta {
Ident::from_meta(meta)
.map(Each::from)
.map(Some)
.map_err(|e| e.with_span(&mnv.value))
} else {
Each::from_meta(meta).map(Some)
}
}
#[derive(Debug, Clone, Default, FromMeta)]
pub struct FieldLevelSetter {
prefix: Option<Ident>,
name: Option<Ident>,
into: Option<bool>,
strip_option: Option<bool>,
skip: Option<bool>,
custom: Option<bool>,
#[darling(with = parse_each)]
each: Option<Each>,
}
impl FieldLevelSetter {
pub fn setter_enabled(&self) -> Option<bool> {
if self.custom.is_some() {
return self.custom.map(|x| !x);
}
self.field_enabled()
}
pub fn field_enabled(&self) -> Option<bool> {
if self.skip.is_some() {
return self.skip.map(|x| !x);
}
if self.prefix.is_some()
|| self.name.is_some()
|| self.into.is_some()
|| self.strip_option.is_some()
|| self.each.is_some()
{
return Some(true);
}
None
}
}
fn field_setter(meta: &Meta) -> darling::Result<FieldLevelSetter> {
if let Meta::Path(_) = meta {
Ok(FieldLevelSetter {
skip: Some(false),
..Default::default()
})
} else {
FieldLevelSetter::from_meta(meta)
}
}
#[derive(Debug, Clone, Default)]
struct FieldForwardedAttrs {
pub field: Vec<Attribute>,
pub setter: Vec<Attribute>,
}
impl TryFrom<Vec<Attribute>> for FieldForwardedAttrs {
type Error = Error;
fn try_from(value: Vec<Attribute>) -> Result<Self, Self::Error> {
let mut result = Self::default();
distribute_and_unnest_attrs(
value,
&mut [
("builder_field_attr", &mut result.field),
("builder_setter_attr", &mut result.setter),
],
)?;
Ok(result)
}
}
#[derive(Debug, Clone, FromField)]
#[darling(
attributes(builder),
forward_attrs(doc, cfg, allow, builder_field_attr, builder_setter_attr),
and_then = "Self::resolve"
)]
pub struct Field {
ident: Option<Ident>,
#[darling(with = TryFrom::try_from)]
attrs: FieldForwardedAttrs,
ty: syn::Type,
pattern: Option<BuilderPattern>,
#[darling(flatten)]
visibility: VisibilityAttr,
#[darling(default, with = field_setter)]
setter: FieldLevelSetter,
default: Option<DefaultExpression>,
try_setter: Flag,
#[darling(default)]
field: FieldLevelFieldMeta,
}
impl Field {
fn resolve(self) -> darling::Result<Self> {
let mut errors = darling::Error::accumulator();
if let Field {
default: Some(field_default),
..
} = &self
{
if self.field.build.is_some() {
errors.push(
darling::Error::custom(
r#"#[builder(default)] and #[builder(field(build="..."))] cannot be used together"#,
)
.with_span(&field_default.span()),
);
}
if self.field.builder_type.is_some() {
errors.push(
darling::Error::custom(
r#"#[builder(default)] and #[builder(field(ty="..."))] cannot be used together"#,
)
.with_span(&field_default.span())
)
}
};
errors.finish_with(self)
}
}
fn distribute_and_unnest_attrs(
mut input: Vec<Attribute>,
outputs: &mut [(&'static str, &mut Vec<Attribute>)],
) -> darling::Result<()> {
let mut errors = vec![];
for (name, list) in &*outputs {
assert!(list.is_empty(), "Output Vec for '{}' was not empty", name);
}
for attr in input.drain(..) {
let destination = outputs
.iter_mut()
.find(|(ptattr, _)| attr.path().is_ident(ptattr));
if let Some((_, destination)) = destination {
match unnest_from_one_attribute(attr) {
Ok(n) => destination.push(n),
Err(e) => errors.push(e),
}
} else {
for (_, output) in outputs.iter_mut() {
output.push(attr.clone());
}
}
}
if !errors.is_empty() {
return Err(darling::Error::multiple(errors));
}
Ok(())
}
fn unnest_from_one_attribute(attr: syn::Attribute) -> darling::Result<Attribute> {
match &attr.style {
syn::AttrStyle::Outer => (),
syn::AttrStyle::Inner(bang) => {
return Err(darling::Error::unsupported_format(&format!(
"{} must be an outer attribute",
attr.path()
.get_ident()
.map(Ident::to_string)
.unwrap_or_else(|| "Attribute".to_string())
))
.with_span(bang));
}
};
let original_span = attr.span();
let pound = attr.pound_token;
let meta = attr.meta;
match meta {
Meta::Path(_) => Err(Error::unsupported_format("word").with_span(&meta)),
Meta::NameValue(_) => Err(Error::unsupported_format("name-value").with_span(&meta)),
Meta::List(list) => {
let inner = list.tokens;
Ok(parse_quote_spanned!(original_span=> #pound [ #inner ]))
}
}
}
fn default_crate_root() -> Path {
parse_quote!(::derive_builder)
}
fn default_create_empty() -> Ident {
Ident::new("create_empty", Span::call_site())
}
#[derive(Debug, Clone, Default)]
struct StructForwardedAttrs {
struct_attrs: Vec<Attribute>,
impl_attrs: Vec<Attribute>,
}
impl TryFrom<Vec<Attribute>> for StructForwardedAttrs {
type Error = Error;
fn try_from(value: Vec<Attribute>) -> Result<Self, Self::Error> {
let mut result = Self::default();
distribute_and_unnest_attrs(
value,
&mut [
("builder_struct_attr", &mut result.struct_attrs),
("builder_impl_attr", &mut result.impl_attrs),
],
)?;
Ok(result)
}
}
#[derive(Debug, Clone, FromDeriveInput)]
#[darling(
attributes(builder),
forward_attrs(cfg, allow, builder_struct_attr, builder_impl_attr),
supports(struct_named)
)]
pub struct Options {
ident: Ident,
#[darling(with = TryFrom::try_from)]
attrs: StructForwardedAttrs,
vis: syn::Visibility,
generics: Generics,
name: Option<Ident>,
#[darling(rename = "crate", default = default_crate_root)]
crate_root: Path,
#[darling(default)]
pattern: BuilderPattern,
#[darling(default)]
build_fn: BuildFn,
#[darling(default)]
derive: PathList,
custom_constructor: Flag,
#[darling(default = default_create_empty)]
create_empty: Ident,
#[darling(default)]
setter: StructLevelSetter,
default: Option<DefaultExpression>,
#[darling(flatten)]
visibility: VisibilityAttr,
data: darling::ast::Data<darling::util::Ignored, Field>,
no_std: Flag,
try_setter: Flag,
#[darling(default)]
field: VisibilityAttr,
}
impl Options {
pub fn builder_ident(&self) -> Ident {
if let Some(ref custom) = self.name {
return custom.clone();
}
format_ident!("{}Builder", self.ident)
}
pub fn builder_error_ident(&self) -> Path {
if let Some(BuildFnError::Existing(existing)) = self.build_fn.error.as_ref() {
existing.clone()
} else if let Some(ref custom) = self.name {
format_ident!("{}Error", custom).into()
} else {
format_ident!("{}BuilderError", self.ident).into()
}
}
pub fn builder_vis(&self) -> Cow<syn::Visibility> {
self.visibility
.to_explicit_visibility()
.unwrap_or_else(|| Cow::Borrowed(&self.vis))
}
pub fn build_method_vis(&self) -> Cow<syn::Visibility> {
self.build_fn
.visibility
.to_explicit_visibility()
.unwrap_or_else(|| self.builder_vis())
}
pub fn raw_fields(&self) -> Vec<&Field> {
self.data
.as_ref()
.take_struct()
.expect("Only structs supported")
.fields
}
pub fn requires_clone(&self) -> bool {
self.pattern.requires_clone() || self.fields().any(|f| f.pattern().requires_clone())
}
pub fn fields(&self) -> FieldIter {
FieldIter(self, self.raw_fields().into_iter())
}
pub fn field_count(&self) -> usize {
self.raw_fields().len()
}
}
impl Options {
pub fn as_builder(&self) -> Builder {
Builder {
crate_root: &self.crate_root,
enabled: true,
ident: self.builder_ident(),
pattern: self.pattern,
derives: &self.derive,
struct_attrs: &self.attrs.struct_attrs,
impl_attrs: &self.attrs.impl_attrs,
impl_default: !self.custom_constructor.is_present(),
create_empty: self.create_empty.clone(),
generics: Some(&self.generics),
visibility: self.builder_vis(),
fields: Vec::with_capacity(self.field_count()),
field_initializers: Vec::with_capacity(self.field_count()),
functions: Vec::with_capacity(self.field_count()),
generate_error: self
.build_fn
.error
.as_ref()
.and_then(BuildFnError::as_existing)
.is_none(),
generate_validation_error: self
.build_fn
.error
.as_ref()
.and_then(BuildFnError::as_generated)
.map(|e| *e.validation_error)
.unwrap_or(true),
no_alloc: cfg!(not(any(feature = "alloc", feature = "lib_has_std"))),
must_derive_clone: self.requires_clone(),
doc_comment: None,
std: !self.no_std.is_present(),
}
}
pub fn as_build_method(&self) -> BuildMethod {
let (_, ty_generics, _) = self.generics.split_for_impl();
BuildMethod {
crate_root: &self.crate_root,
enabled: !self.build_fn.skip,
ident: &self.build_fn.name,
visibility: self.build_method_vis(),
pattern: self.pattern,
target_ty: &self.ident,
target_ty_generics: Some(ty_generics),
error_ty: self.builder_error_ident(),
initializers: Vec::with_capacity(self.field_count()),
doc_comment: None,
default_struct: self.default.as_ref(),
validate_fn: self.build_fn.validate.as_ref(),
}
}
}
pub struct FieldWithDefaults<'a> {
parent: &'a Options,
field: &'a Field,
}
impl<'a> FieldWithDefaults<'a> {
pub fn setter_enabled(&self) -> bool {
self.field
.setter
.setter_enabled()
.or_else(|| self.parent.setter.enabled())
.unwrap_or(true)
}
pub fn field_enabled(&self) -> bool {
self.field
.setter
.field_enabled()
.or_else(|| self.parent.setter.enabled())
.unwrap_or(true)
}
pub fn try_setter(&self) -> bool {
self.field.try_setter.is_present() || self.parent.try_setter.is_present()
}
pub fn setter_prefix(&self) -> Option<&Ident> {
self.field
.setter
.prefix
.as_ref()
.or(self.parent.setter.prefix.as_ref())
}
pub fn setter_ident(&self) -> syn::Ident {
if let Some(ref custom) = self.field.setter.name {
return custom.clone();
}
let ident = &self.field.ident;
if let Some(ref prefix) = self.setter_prefix() {
return format_ident!("{}_{}", prefix, ident.as_ref().unwrap());
}
ident.clone().unwrap()
}
pub fn setter_into(&self) -> bool {
self.field
.setter
.into
.or(self.parent.setter.into)
.unwrap_or_default()
}
pub fn setter_strip_option(&self) -> bool {
self.field
.setter
.strip_option
.or(self.parent.setter.strip_option)
.unwrap_or_default()
}
pub fn setter_vis(&self) -> Cow<syn::Visibility> {
self.field
.visibility
.to_explicit_visibility()
.or_else(|| self.parent.visibility.to_explicit_visibility())
.unwrap_or_else(|| Cow::Owned(syn::parse_quote!(pub)))
}
pub fn field_ident(&self) -> &syn::Ident {
self.field
.ident
.as_ref()
.expect("Tuple structs are not supported")
}
pub fn field_vis(&self) -> Cow<syn::Visibility> {
self.field
.field
.visibility
.to_explicit_visibility()
.or_else(
|| {
if self.field_enabled() {
None
} else {
Some(Cow::Owned(syn::Visibility::Inherited))
}
},
)
.or_else(|| self.parent.field.to_explicit_visibility())
.unwrap_or(Cow::Owned(syn::Visibility::Inherited))
}
pub fn field_type(&'a self) -> BuilderFieldType<'a> {
if !self.field_enabled() {
BuilderFieldType::Phantom(&self.field.ty)
} else if let Some(custom_ty) = self.field.field.builder_type.as_ref() {
BuilderFieldType::Precise(custom_ty)
} else {
BuilderFieldType::Optional(&self.field.ty)
}
}
pub fn conversion(&'a self) -> FieldConversion<'a> {
match (&self.field.field.builder_type, &self.field.field.build) {
(_, Some(block)) => FieldConversion::Block(block),
(Some(_), None) => FieldConversion::Move,
(None, None) => FieldConversion::OptionOrDefault,
}
}
pub fn pattern(&self) -> BuilderPattern {
self.field.pattern.unwrap_or(self.parent.pattern)
}
pub fn use_parent_default(&self) -> bool {
self.field.default.is_none() && self.parent.default.is_some()
}
}
impl<'a> FieldWithDefaults<'a> {
pub fn as_setter(&'a self) -> Setter<'a> {
Setter {
crate_root: &self.parent.crate_root,
setter_enabled: self.setter_enabled(),
try_setter: self.try_setter(),
visibility: self.setter_vis(),
pattern: self.pattern(),
attrs: &self.field.attrs.setter,
ident: self.setter_ident(),
field_ident: self.field_ident(),
field_type: self.field_type(),
generic_into: self.setter_into(),
strip_option: self.setter_strip_option(),
each: self.field.setter.each.as_ref(),
}
}
pub fn as_initializer(&'a self) -> Initializer<'a> {
Initializer {
crate_root: &self.parent.crate_root,
field_enabled: self.field_enabled(),
field_ident: self.field_ident(),
builder_pattern: self.pattern(),
default_value: self.field.default.as_ref(),
use_default_struct: self.use_parent_default(),
conversion: self.conversion(),
custom_error_type_span: self.parent.build_fn.error.as_ref().and_then(|err_ty| {
match err_ty {
BuildFnError::Existing(p) => Some(p.span()),
_ => None,
}
}),
}
}
pub fn as_builder_field(&'a self) -> BuilderField<'a> {
BuilderField {
crate_root: &self.parent.crate_root,
field_ident: self.field_ident(),
field_type: self.field_type(),
field_visibility: self.field_vis(),
attrs: &self.field.attrs.field,
}
}
}
pub struct FieldIter<'a>(&'a Options, IntoIter<&'a Field>);
impl<'a> Iterator for FieldIter<'a> {
type Item = FieldWithDefaults<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.1.next().map(|field| FieldWithDefaults {
parent: self.0,
field,
})
}
}