Skip to content

Commit

Permalink
Change FlagVisibility to support explicit visibilities
Browse files Browse the repository at this point in the history
* Use Cow<syn::Visibility> to avoid unnecessary cloning
* Make as_expressed_vis return a Result
* Add Visibility::explicit
  • Loading branch information
TedDriggs committed Apr 6, 2022
1 parent 38610eb commit 29f074b
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 35 deletions.
6 changes: 4 additions & 2 deletions derive_builder_core/src/build_method.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use doc_comment_from;
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
Expand Down Expand Up @@ -43,7 +45,7 @@ pub struct BuildMethod<'a> {
/// Name of this build fn.
pub ident: &'a syn::Ident,
/// Visibility of the build method, e.g. `syn::Visibility::Public`.
pub visibility: syn::Visibility,
pub visibility: Cow<'a, syn::Visibility>,
/// How the build method takes and returns `self` (e.g. mutably).
pub pattern: BuilderPattern,
/// Type of the target field.
Expand Down Expand Up @@ -138,7 +140,7 @@ macro_rules! default_build_method {
BuildMethod {
enabled: true,
ident: &syn::Ident::new("build", ::proc_macro2::Span::call_site()),
visibility: syn::parse_quote!(pub),
visibility: ::std::borrow::Cow::Owned(syn::parse_quote!(pub)),
pattern: BuilderPattern::Mutable,
target_ty: &syn::Ident::new("Foo", ::proc_macro2::Span::call_site()),
target_ty_generics: None,
Expand Down
6 changes: 4 additions & 2 deletions derive_builder_core/src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use proc_macro2::TokenStream;
use quote::{format_ident, ToTokens, TokenStreamExt};
use syn::punctuated::Punctuated;
Expand Down Expand Up @@ -110,7 +112,7 @@ pub struct Builder<'a> {
/// definition.
pub generics: Option<&'a syn::Generics>,
/// Visibility of the builder struct, e.g. `syn::Visibility::Public`.
pub visibility: syn::Visibility,
pub visibility: Cow<'a, syn::Visibility>,
/// Fields of the builder struct, e.g. `foo: u32,`
///
/// Expects each entry to be terminated by a comma.
Expand Down Expand Up @@ -341,7 +343,7 @@ macro_rules! default_builder {
impl_default: true,
create_empty: syn::Ident::new("create_empty", ::proc_macro2::Span::call_site()),
generics: None,
visibility: parse_quote!(pub),
visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)),
fields: vec![quote!(foo: u32,)],
field_initializers: vec![quote!(foo: ::derive_builder::export::core::default::Default::default(), )],
functions: vec![quote!(fn bar() -> { unimplemented!() })],
Expand Down
8 changes: 5 additions & 3 deletions derive_builder_core/src/builder_field.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use proc_macro2::TokenStream;
use quote::{ToTokens, TokenStreamExt};
use syn;
Expand Down Expand Up @@ -42,7 +44,7 @@ pub struct BuilderField<'a> {
/// least for now.
pub field_enabled: bool,
/// Visibility of this builder field, e.g. `syn::Visibility::Public`.
pub field_visibility: syn::Visibility,
pub field_visibility: Cow<'a, syn::Visibility>,
/// Attributes which will be attached to this builder field.
pub attrs: &'a [syn::Attribute],
}
Expand Down Expand Up @@ -88,7 +90,7 @@ macro_rules! default_builder_field {
field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
field_type: &parse_quote!(String),
field_enabled: true,
field_visibility: parse_quote!(pub),
field_visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)),
attrs: &[parse_quote!(#[some_attr])],
}
}};
Expand Down Expand Up @@ -129,7 +131,7 @@ mod tests {

#[test]
fn private_field() {
let private = syn::Visibility::Inherited;
let private = Cow::Owned(syn::Visibility::Inherited);
let mut field = default_builder_field!();
field.field_visibility = private;

Expand Down
92 changes: 67 additions & 25 deletions derive_builder_core/src/macro_options/darling_opts.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::vec::IntoIter;
use std::{borrow::Cow, vec::IntoIter};

use crate::BuildMethod;

use darling::util::{Flag, PathList};
use darling::{self, FromMeta};
use darling::{self, Error, FromMeta};
use proc_macro2::{Span, TokenStream};
use syn::parse::{ParseStream, Parser};
use syn::Meta;
use syn::{self, spanned::Spanned, Attribute, Generics, Ident, Path, Visibility};
use syn::{self, spanned::Spanned, Attribute, Generics, Ident, Path};

use crate::{
Builder, BuilderField, BuilderPattern, DefaultExpression, DeprecationNotes, Each, Initializer,
Expand All @@ -16,23 +16,44 @@ use crate::{

/// `derive_builder` uses separate sibling keywords to represent
/// mutually-exclusive visibility states. This trait requires implementers to
/// expose those flags and provides a method to compute any explicit visibility
/// expose those property values and provides a method to compute any explicit visibility
/// bounds.
trait FlagVisibility {
trait Visibility {
fn public(&self) -> &Flag;
fn private(&self) -> &Flag;
fn explicit(&self) -> Option<&syn::Visibility>;

/// Get the explicitly-expressed visibility preference from the attribute.
/// This returns `None` if the input didn't include either keyword.
///
/// # Panics
/// This method panics if the input specifies both `public` and `private`.
fn as_expressed_vis(&self) -> Option<Visibility> {
match (self.public().is_some(), self.private().is_some()) {
(true, true) => panic!("A field cannot be both public and private"),
(true, false) => Some(syn::parse_quote!(pub)),
(false, true) => Some(Visibility::Inherited),
(false, false) => None,
fn as_expressed_vis(&self) -> darling::Result<Option<Cow<syn::Visibility>>> {
let declares_public = self.public().is_some();
let declares_private = self.private().is_some();

// Start with `vis = "..."` because it will have a span for the error message.
if let Some(vis) = self.explicit() {
if declares_public || declares_private {
Err(
Error::custom(r#"`vis="..."` cannot be used with `public` or `private`"#)
.with_span(vis),
)
} else {
Ok(Some(Cow::Borrowed(vis)))
}
} else if declares_public {
if declares_private {
Err(Error::custom(
r#"`public` and `private` cannot be used together"#,
))
} else {
Ok(Some(Cow::Owned(syn::parse_quote!(pub))))
}
} else if declares_private {
Ok(Some(Cow::Owned(syn::Visibility::Inherited)))
} else {
Ok(None)
}
}
}
Expand Down Expand Up @@ -77,14 +98,18 @@ impl Default for BuildFn {
}
}

impl FlagVisibility for BuildFn {
impl Visibility for BuildFn {
fn public(&self) -> &Flag {
&self.public
}

fn private(&self) -> &Flag {
&self.private
}

fn explicit(&self) -> Option<&syn::Visibility> {
None // TODO
}
}

/// Contents of the `field` meta in `builder` attributes.
Expand All @@ -95,14 +120,18 @@ pub struct FieldMeta {
private: Flag,
}

impl FlagVisibility for FieldMeta {
impl Visibility for FieldMeta {
fn public(&self) -> &Flag {
&self.public
}

fn private(&self) -> &Flag {
&self.private
}

fn explicit(&self) -> Option<&syn::Visibility> {
None // TODO
}
}

#[derive(Debug, Clone, Default, FromMeta)]
Expand Down Expand Up @@ -361,14 +390,18 @@ fn unnest_from_one_attribute(attr: syn::Attribute) -> darling::Result<Attribute>
Ok(attr)
}

impl FlagVisibility for Field {
impl Visibility for Field {
fn public(&self) -> &Flag {
&self.public
}

fn private(&self) -> &Flag {
&self.private
}

fn explicit(&self) -> Option<&syn::Visibility> {
None // TODO
}
}

fn default_create_empty() -> Ident {
Expand Down Expand Up @@ -398,7 +431,7 @@ pub struct Options {
#[darling(skip)]
impl_attrs: Vec<Attribute>,

vis: Visibility,
vis: syn::Visibility,

generics: Generics,

Expand Down Expand Up @@ -456,14 +489,18 @@ pub struct Options {
deprecation_notes: DeprecationNotes,
}

impl FlagVisibility for Options {
impl Visibility for Options {
fn public(&self) -> &Flag {
&self.public
}

fn private(&self) -> &Flag {
&self.private
}

fn explicit(&self) -> Option<&syn::Visibility> {
None // TODO
}
}

impl Options {
Expand Down Expand Up @@ -504,15 +541,18 @@ impl Options {
/// The visibility of the builder struct.
/// If a visibility was declared in attributes, that will be used;
/// otherwise the struct's own visibility will be used.
pub fn builder_vis(&self) -> Visibility {
self.as_expressed_vis().unwrap_or_else(|| self.vis.clone())
pub fn builder_vis(&self) -> Cow<syn::Visibility> {
self.as_expressed_vis()
.unwrap()
.unwrap_or(Cow::Borrowed(&self.vis))
}

/// Get the visibility of the emitted `build` method.
/// This defaults to the visibility of the parent builder, but can be overridden.
pub fn build_method_vis(&self) -> Visibility {
pub fn build_method_vis(&self) -> Cow<syn::Visibility> {
self.build_fn
.as_expressed_vis()
.unwrap()
.unwrap_or_else(|| self.builder_vis())
}

Expand Down Expand Up @@ -669,11 +709,12 @@ impl<'a> FieldWithDefaults<'a> {
}

/// Get the visibility of the emitted setter, if there will be one.
pub fn setter_vis(&self) -> Visibility {
pub fn setter_vis(&self) -> Cow<syn::Visibility> {
self.field
.as_expressed_vis()
.or_else(|| self.parent.as_expressed_vis())
.unwrap_or_else(|| syn::parse_quote!(pub))
.unwrap()
.or_else(|| self.parent.as_expressed_vis().unwrap())
.unwrap_or_else(|| Cow::Owned(syn::parse_quote!(pub)))
}

/// Get the ident of the input field. This is also used as the ident of the
Expand All @@ -685,12 +726,13 @@ impl<'a> FieldWithDefaults<'a> {
.expect("Tuple structs are not supported")
}

pub fn field_vis(&self) -> Visibility {
pub fn field_vis(&self) -> Cow<syn::Visibility> {
self.field
.field
.as_expressed_vis()
.or_else(|| self.parent.field.as_expressed_vis())
.unwrap_or(Visibility::Inherited)
.unwrap()
.or_else(|| self.parent.field.as_expressed_vis().unwrap())
.unwrap_or(Cow::Owned(syn::Visibility::Inherited))
}

pub fn pattern(&self) -> BuilderPattern {
Expand Down
8 changes: 5 additions & 3 deletions derive_builder_core/src/setter.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#![allow(clippy::useless_let_if_seq)]
use std::borrow::Cow;

use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use syn;
Expand Down Expand Up @@ -43,7 +45,7 @@ pub struct Setter<'a> {
/// Enables code generation for the `try_` variant of this setter fn.
pub try_setter: bool,
/// Visibility of the setter, e.g. `syn::Visibility::Public`.
pub visibility: syn::Visibility,
pub visibility: Cow<'a, syn::Visibility>,
/// How the setter method takes and returns `self` (e.g. mutably).
pub pattern: BuilderPattern,
/// Attributes which will be attached to this setter fn.
Expand Down Expand Up @@ -266,7 +268,7 @@ macro_rules! default_setter {
Setter {
setter_enabled: true,
try_setter: false,
visibility: parse_quote!(pub),
visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)),
pattern: BuilderPattern::Mutable,
attrs: &vec![],
ident: syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
Expand Down Expand Up @@ -344,7 +346,7 @@ mod tests {

#[test]
fn private() {
let vis = syn::Visibility::Inherited;
let vis = Cow::Owned(syn::Visibility::Inherited);

let mut setter = default_setter!();
setter.visibility = vis;
Expand Down

0 comments on commit 29f074b

Please sign in to comment.