Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add #[builder(crate = "...")] for re-export scenarios #275

Merged
merged 3 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions derive_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
16 changes: 16 additions & 0 deletions derive_builder/tests/compile-fail/crate_root.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#[macro_use]
extern crate derive_builder;

// This is a stand-in for any bad crate directive
mod empty {}

#[derive(Builder)]
// It would be nice if the "failed to resolve" errors would identify `"empty"` as the error span,
// but doing so would require rewriting a lot of the code generation to use the crate_root span
// for the full path of the thing being imported, and that doesn't seem worth the code churn.
#[builder(crate = "empty")]
struct BadCrate {
lorem: String,
}

fn main() {}
97 changes: 97 additions & 0 deletions derive_builder/tests/compile-fail/crate_root.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
error[E0433]: failed to resolve: could not find `export` in `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ could not find `export` in `empty`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0433]: failed to resolve: could not find `export` in `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ not found in `empty::export::core::option`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing this enum
|
5 | use derive_builder::export::core::option::Option;
|

error[E0433]: failed to resolve: could not find `export` in `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ not found in `empty::export::core::clone`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing this trait
|
5 | use derive_builder::export::core::clone::Clone;
|

error[E0433]: failed to resolve: could not find `export` in `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ not found in `empty::export::core::result`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing one of these items
|
5 | use derive_builder::export::core::fmt::Result;
|
5 | use derive_builder::export::core::io::Result;
|
5 | use derive_builder::export::core::result::Result;
|
5 | use derive_builder::export::core::thread::Result;
|

error[E0433]: failed to resolve: could not find `export` in `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ not found in `empty::export::core::convert`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing this trait
|
5 | use derive_builder::export::core::convert::Into;
|

error[E0433]: failed to resolve: could not find `UninitializedFieldError` in `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ not found in `empty`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing this struct
|
5 | use derive_builder::UninitializedFieldError;
|

error[E0433]: failed to resolve: could not find `export` in `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ not found in `empty::export::core::default`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing this trait
|
5 | use derive_builder::export::core::default::Default;
|

error[E0412]: cannot find type `UninitializedFieldError` in module `empty`
--> tests/compile-fail/crate_root.rs:7:10
|
7 | #[derive(Builder)]
| ^^^^^^^ not found in `empty`
|
= note: this error originates in the derive macro `Builder` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider importing this struct
|
5 | use derive_builder::UninitializedFieldError;
|
24 changes: 24 additions & 0 deletions derive_builder/tests/run-pass/crate_alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! Test that an alias of derive_builder is valid in #[builder(crate = "...")]
//!
//! This test is imperfect, as it still passes without setting `crate = "..."`.
//! This is likely because `derive_builder` is automatically present in 2018
//! without needing the extern crate line.
//!
//! The test will fail if an incorrect alias is used, so it adds limited value.

#[macro_use]
extern crate derive_builder as db;

#[derive(Builder)]
#[builder(crate = "db")]
struct AliasedCrate {
#[builder(setter(into))]
lorem: String,
}

fn main() {
AliasedCrateBuilder::default()
.lorem("hello")
.build()
.unwrap();
}
17 changes: 12 additions & 5 deletions derive_builder_core/src/build_method.rs
Original file line number Diff line number Diff line change
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,9 @@ impl<'a> BuildMethod<'a> {
macro_rules! default_build_method {
() => {
BuildMethod {
// Deliberately don't use the default value here - make sure
// that all test cases are passing crate_root through properly.
crate_root: &parse_quote!(::db),
enabled: true,
ident: &syn::Ident::new("build", ::proc_macro2::Span::call_site()),
visibility: ::std::borrow::Cow::Owned(syn::parse_quote!(pub)),
Expand Down Expand Up @@ -166,7 +173,7 @@ mod tests {
assert_eq!(
quote!(#build_method).to_string(),
quote!(
pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
pub fn build(&self) -> ::db::export::core::result::Result<Foo, FooBuilderError> {
Ok(Foo {
foo: self.foo,
})
Expand All @@ -187,7 +194,7 @@ mod tests {
assert_eq!(
quote!(#build_method).to_string(),
quote!(
pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
pub fn build(&self) -> ::db::export::core::result::Result<Foo, FooBuilderError> {
let __default: Foo = { Default::default() };
Ok(Foo {
foo: self.foo,
Expand Down Expand Up @@ -217,7 +224,7 @@ mod tests {
assert_eq!(
quote!(#build_method).to_string(),
quote!(
pub fn finish(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
pub fn finish(&self) -> ::db::export::core::result::Result<Foo, FooBuilderError> {
Ok(Foo {
foo: self.foo,
})
Expand All @@ -238,7 +245,7 @@ mod tests {
assert_eq!(
quote!(#build_method).to_string(),
quote!(
pub fn build(&self) -> ::derive_builder::export::core::result::Result<Foo, FooBuilderError> {
pub fn build(&self) -> ::db::export::core::result::Result<Foo, FooBuilderError> {
IpsumBuilder::validate(&self)?;

Ok(Foo {
Expand Down