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

Support grouping all builder types under a separate module #214

Open
Veetaha opened this issue Nov 22, 2024 · 0 comments
Open

Support grouping all builder types under a separate module #214

Veetaha opened this issue Nov 22, 2024 · 0 comments
Labels
design needed The feature requires more design effort feature request A new feature is requested

Comments

@Veetaha
Copy link
Collaborator

Veetaha commented Nov 22, 2024

Problem Statement

The original conversation about this happened in ayrat555/frankenstein#231 (comment).

The problem is that builders are sometimes more of a noise in the docs. Having builder types appear in the docs adjacently to other more important types doesn't always make sense.

It may make sense to group builder types under a separate module in large-scale scenarios. These are cases that involve hundreds of structs with #[derive(Builder)].

Solution

We need a solution for this that would generate a module structure like the following one:

pub mod builders {
    pub use super::internal::{FooBuilder, /* ... */};
}

pub use internal::{Foo, /* ... */};

mod internal {
    #[derive(Builder)]
    pub struct Foo { /**/ }
    
    /* ... */
}

How can we achieve this? I see several ways to tackle this problem

Workaround

It's possible to achieve this in the current state of bon (3.0.2 at the time of this writing) with the following declarative macro (it requires paste and macro_rules_attribute):

macro_rules! builders {
    (
        mod __inline__ {
            $(
                $(#[$attr:meta])*
                pub struct $name:ident { $($body:tt)* }
            )*
        }
    ) => {
        pub mod builders {
            ::paste::paste! {
                #[doc(inline)]
                pub use super::internal::{ $( [<$name Builder>], )* };
            }
        }

        #[doc(inline)]
        pub use internal::{ $($name,)* };

        mod internal {
            use super::*;
            $(
                #[derive(bon::Builder)]
                $(#[$attr])*
                pub struct $name { $($body)* }
            )*
        }
    };
}

#[macro_rules_attribute::apply(builders!)]
mod __inline__ {
    pub struct Foo { /**/ }
}

Here __inline__ isn't anything special. We just need to give a name to the module the builders macro is applied to. That module will be flattened by the macro. In theory, a cleaner solution would be to make it into a function-like macro builders! { }, but rustfmt doesn't format code inside of {} of the function-like macro, unfortunately.

Official macro?

There could an official proc macro attribute that would be more robust than the workaround declarative macro suggested higher. It would handle non-struct items (e.g. functions and methods). It would also accept variable visibility for items (not just pub), and would provide better syntax errors handling for better IDE experience.

Codegen?

People could use external code generation CLI, that would directly create a Rust file with the structs with builder derives. That would be very ad-hoc and inconvenient, since one would need to keep the generated code fresh in git sources.

Is there enough demand for this to provide an official API for this?

I'm not sure how I feel about exposing a builders macros from bon yet. This isn't something any builder crate has done yet in the history of ~8 years (derive_builder age). Maybe it's not as big of a problem, so that people just didn't solve it. Also, the workaround with the declarative macro may be just good enough for a solution here.

I'm ready to be proven wrong though. I'd just like to avoid exposing API that would be very limited, which isn't particularly nice at the call site (due to the nesting and the dummy module name __inline__).

A note for the community from the maintainers

Please vote on this issue by adding a 👍 reaction to help the maintainers with prioritizing it. You may add a comment describing your real use case related to this issue for us to better understand the problem domain.

@Veetaha Veetaha added design needed The feature requires more design effort feature request A new feature is requested labels Nov 22, 2024
@Veetaha Veetaha changed the title Group all builder types under a separate module Support grouping all builder types under a separate module Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design needed The feature requires more design effort feature request A new feature is requested
Projects
None yet
Development

No branches or pull requests

1 participant