Skip to content

Commit

Permalink
Add #[builder(crate = "...")] at struct level
Browse files Browse the repository at this point in the history
This allows setting the crate root for re-export scenarios.

All hardcoded usage of `::derive_builder` in generated code has now
been replaced by a `crate_root` variable, which contains the path to use.

For backwards compatibility, this defaults to `::derive_builder`.

Fixes #274
  • Loading branch information
TedDriggs committed Nov 22, 2022
1 parent 53fd62f commit 630c98b
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 61 deletions.
1 change: 1 addition & 0 deletions derive_builder/CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## Unreleased
- Produce error when `default` is used with `field(type = "...")` rather than silently ignoring `default` #269
- Add support for `crate = "..."` to support re-export scenarios #274

## [0.11.2] - 2022-04-20
- Allow restricted visibility using `vis = "..."` for builders, build methods, setters, and fields #247
Expand Down
1 change: 1 addition & 0 deletions derive_builder/README.md
Expand Up @@ -130,6 +130,7 @@ It's as simple as three steps:
- **Builder derivations**: You can use `#[builder(derive(Trait1, Trait2, ...))]` to have the builder derive additonal traits. All builders derive `Default` and `Clone`, so you should not declare those in this attribute.
- **Pass-through attributes**: Use `#[builder_struct_attr(...)]`, `#[builder_impl_attr(...)]`, `#[builder_field_attr(...)]`, and `#[builder_setter_attr(...)]` to declare attributes that will be added to the relevant part of the generated builder.
- **no_std support**: Just add `#[builder(no_std)]` to your struct and add `extern crate alloc` to your crate.
- **Re-export support**: Use `#[builder(crate = "...")]` to set the root for `derive_builder`. This is useful if your crate is re-exporting `derive_builder::Builder` and needs the generated code to not directly reference the `derive_builder` crate.

For more information and examples please take a look at our [documentation][doc].

Expand Down
7 changes: 6 additions & 1 deletion derive_builder_core/src/build_method.rs
Expand Up @@ -40,6 +40,8 @@ use crate::DefaultExpression;
/// ```
#[derive(Debug)]
pub struct BuildMethod<'a> {
/// Path to the root of the derive_builder crate.
pub crate_root: &'a syn::Path,
/// Enables code generation for this build method.
pub enabled: bool,
/// Name of this build fn.
Expand Down Expand Up @@ -82,6 +84,7 @@ impl<'a> ToTokens for BuildMethod<'a> {
};
let doc_comment = &self.doc_comment;
let default_struct = self.default_struct.as_ref().map(|default_expr| {
let default_expr = default_expr.with_crate_root(self.crate_root);
let ident = syn::Ident::new(DEFAULT_STRUCT_NAME, Span::call_site());
quote!(let #ident: #target_ty #target_ty_generics = #default_expr;)
});
Expand All @@ -92,10 +95,11 @@ impl<'a> ToTokens for BuildMethod<'a> {
let error_ty = &self.error_ty;

if self.enabled {
let crate_root = &self.crate_root;
tokens.append_all(quote!(
#doc_comment
#vis fn #ident(#self_param)
-> ::derive_builder::export::core::result::Result<#target_ty #target_ty_generics, #error_ty>
-> #crate_root::export::core::result::Result<#target_ty #target_ty_generics, #error_ty>
{
#validate_fn
#default_struct
Expand Down Expand Up @@ -138,6 +142,7 @@ impl<'a> BuildMethod<'a> {
macro_rules! default_build_method {
() => {
BuildMethod {
crate_root: &parse_quote!(::derive_builder),
enabled: true,
ident: &syn::Ident::new("build", ::proc_macro2::Span::call_site()),
visibility: ::std::borrow::Cow::Owned(syn::parse_quote!(pub)),
Expand Down
24 changes: 15 additions & 9 deletions derive_builder_core/src/builder.rs
Expand Up @@ -86,6 +86,8 @@ use Setter;
/// ```
#[derive(Debug)]
pub struct Builder<'a> {
/// Path to the root of the derive_builder crate.
pub crate_root: &'a Path,
/// Enables code generation for this builder struct.
pub enabled: bool,
/// Name of this builder struct.
Expand Down Expand Up @@ -143,6 +145,7 @@ pub struct Builder<'a> {
impl<'a> ToTokens for Builder<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
if self.enabled {
let crate_root = self.crate_root;
let builder_vis = &self.visibility;
let builder_ident = &self.ident;
let bounded_generics = self.compute_impl_bounds();
Expand Down Expand Up @@ -216,7 +219,7 @@ impl<'a> ToTokens for Builder<'a> {

if self.impl_default {
tokens.append_all(quote!(
impl #impl_generics ::derive_builder::export::core::default::Default for #builder_ident #ty_generics #where_clause {
impl #impl_generics #crate_root::export::core::default::Default for #builder_ident #ty_generics #where_clause {
fn default() -> Self {
Self::#create_empty()
}
Expand All @@ -236,23 +239,23 @@ impl<'a> ToTokens for Builder<'a> {
/// Uninitialized field
UninitializedField(&'static str),
/// Custom validation error
ValidationError(::derive_builder::export::core::string::String),
ValidationError(#crate_root::export::core::string::String),
}

impl ::derive_builder::export::core::convert::From<::derive_builder::UninitializedFieldError> for #builder_error_ident {
fn from(s: ::derive_builder::UninitializedFieldError) -> Self {
impl #crate_root::export::core::convert::From<#crate_root::UninitializedFieldError> for #builder_error_ident {
fn from(s: #crate_root::UninitializedFieldError) -> Self {
Self::UninitializedField(s.field_name())
}
}

impl ::derive_builder::export::core::convert::From<::derive_builder::export::core::string::String> for #builder_error_ident {
fn from(s: ::derive_builder::export::core::string::String) -> Self {
impl #crate_root::export::core::convert::From<#crate_root::export::core::string::String> for #builder_error_ident {
fn from(s: #crate_root::export::core::string::String) -> Self {
Self::ValidationError(s)
}
}

impl ::derive_builder::export::core::fmt::Display for #builder_error_ident {
fn fmt(&self, f: &mut ::derive_builder::export::core::fmt::Formatter) -> ::derive_builder::export::core::fmt::Result {
impl #crate_root::export::core::fmt::Display for #builder_error_ident {
fn fmt(&self, f: &mut #crate_root::export::core::fmt::Formatter) -> #crate_root::export::core::fmt::Result {
match self {
Self::UninitializedField(ref field) => write!(f, "`{}` must be initialized", field),
Self::ValidationError(ref error) => write!(f, "{}", error),
Expand Down Expand Up @@ -309,11 +312,13 @@ impl<'a> Builder<'a> {
return generics;
}

let crate_root = self.crate_root;

let clone_bound = TypeParamBound::Trait(TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path: syn::parse_quote!(::derive_builder::export::core::clone::Clone),
path: syn::parse_quote!(#crate_root::export::core::clone::Clone),
});

for typ in generics.type_params_mut() {
Expand All @@ -334,6 +339,7 @@ impl<'a> Builder<'a> {
macro_rules! default_builder {
() => {
Builder {
crate_root: &parse_quote!(::derive_builder),
enabled: true,
ident: syn::Ident::new("FooBuilder", ::proc_macro2::Span::call_site()),
pattern: Default::default(),
Expand Down
29 changes: 23 additions & 6 deletions derive_builder_core/src/builder_field.rs
Expand Up @@ -31,6 +31,8 @@ use syn;
/// ```
#[derive(Debug, Clone)]
pub struct BuilderField<'a> {
/// Path to the root of the derive_builder crate.
pub crate_root: &'a syn::Path,
/// Name of the target field.
pub field_ident: &'a syn::Ident,
/// Type of the builder field.
Expand All @@ -45,7 +47,7 @@ impl<'a> ToTokens for BuilderField<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let ident = self.field_ident;
let vis = &self.field_visibility;
let ty = &self.field_type;
let ty = &self.field_type.with_crate_root(self.crate_root);
let attrs = self.attrs;
tokens.append_all(quote!(
#(#attrs)* #vis #ident: #ty,
Expand All @@ -57,7 +59,8 @@ impl<'a> BuilderField<'a> {
/// Emits a struct field initializer that initializes the field to `Default::default`.
pub fn default_initializer_tokens(&self) -> TokenStream {
let ident = self.field_ident;
quote! { #ident : ::derive_builder::export::core::default::Default::default(), }
let crate_root = self.crate_root;
quote! { #ident : #crate_root::export::core::default::Default::default(), }
}
}

Expand Down Expand Up @@ -94,17 +97,30 @@ impl<'a> BuilderFieldType<'a> {
BuilderFieldType::Phantom(_ty) => panic!("phantom fields should never have setters"),
}
}

fn with_crate_root(&'a self, crate_root: &'a syn::Path) -> BuilderFieldTypeWithCrateRoot<'a> {
BuilderFieldTypeWithCrateRoot {
crate_root,
field_type: self,
}
}
}

impl<'a> ToTokens for BuilderFieldType<'a> {
struct BuilderFieldTypeWithCrateRoot<'a> {
crate_root: &'a syn::Path,
field_type: &'a BuilderFieldType<'a>,
}

impl<'a> ToTokens for BuilderFieldTypeWithCrateRoot<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
let crate_root = self.crate_root;
match self.field_type {
BuilderFieldType::Optional(ty) => tokens.append_all(quote!(
::derive_builder::export::core::option::Option<#ty>
#crate_root::export::core::option::Option<#ty>
)),
BuilderFieldType::Precise(ty) => ty.to_tokens(tokens),
BuilderFieldType::Phantom(ty) => tokens.append_all(quote!(
::derive_builder::export::core::marker::PhantomData<#ty>
#crate_root::export::core::marker::PhantomData<#ty>
)),
}
}
Expand All @@ -118,6 +134,7 @@ impl<'a> ToTokens for BuilderFieldType<'a> {
macro_rules! default_builder_field {
() => {{
BuilderField {
crate_root: &parse_quote!(::derive_builder),
field_ident: &syn::Ident::new("foo", ::proc_macro2::Span::call_site()),
field_type: BuilderFieldType::Optional(Box::leak(Box::new(parse_quote!(String)))),
field_visibility: ::std::borrow::Cow::Owned(parse_quote!(pub)),
Expand Down
45 changes: 38 additions & 7 deletions derive_builder_core/src/default_expression.rs
@@ -1,5 +1,6 @@
use crate::BlockContents;
use quote::{ToTokens, TokenStreamExt};
use proc_macro2::Span;
use quote::ToTokens;

/// A `DefaultExpression` can be either explicit or refer to the canonical trait.
#[derive(Debug, Clone)]
Expand All @@ -9,6 +10,19 @@ pub enum DefaultExpression {
}

impl DefaultExpression {
/// Add the crate root path so the default expression can be emitted
/// to a `TokenStream`.
///
/// This function is needed because the crate root is inherited from the container, so it cannot
/// be provided at parse time to [`darling::FromMeta::from_word`] when reading, and [`ToTokens`] does not
/// accept any additional parameters, so it annot be provided at emit time.
pub fn with_crate_root<'a>(&'a self, crate_root: &'a syn::Path) -> impl 'a + ToTokens {
DefaultExpressionWithCrateRoot {
crate_root,
expr: self,
}
}

#[cfg(test)]
pub fn explicit<I: Into<BlockContents>>(content: I) -> Self {
DefaultExpression::Explicit(content.into())
Expand All @@ -25,13 +39,30 @@ impl darling::FromMeta for DefaultExpression {
}
}

impl ToTokens for DefaultExpression {
impl syn::spanned::Spanned for DefaultExpression {
fn span(&self) -> Span {
match self {
DefaultExpression::Explicit(block) => block.span(),
DefaultExpression::Trait => Span::call_site(),
}
}
}

/// Wrapper for `DefaultExpression`
struct DefaultExpressionWithCrateRoot<'a> {
crate_root: &'a syn::Path,
expr: &'a DefaultExpression,
}

impl<'a> ToTokens for DefaultExpressionWithCrateRoot<'a> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match *self {
Self::Explicit(ref block) => block.to_tokens(tokens),
Self::Trait => tokens.append_all(quote!(
::derive_builder::export::core::default::Default::default()
)),
let crate_root = self.crate_root;
match self.expr {
DefaultExpression::Explicit(ref block) => block.to_tokens(tokens),
DefaultExpression::Trait => quote!(
#crate_root::export::core::default::Default::default()
)
.to_tokens(tokens),
}
}
}

0 comments on commit 630c98b

Please sign in to comment.