lib.rs
Overview
This file implements a procedural macro named Setters that automatically generates setter methods for struct fields in Rust. It provides extensive customization options through a set of attributes that control how setters are generated, their naming, behavior, and integration with delegation patterns. The macro supports both public and private fields, different ownership models for self, and can optionally generate tracing calls or custom post-processing logic.
The core functionality revolves around parsing attributes on structs and fields, constructing internal representations of configuration (ContainerDef and FieldDef), and emitting Rust code that defines the setter methods accordingly. The file uses the darling crate for attribute parsing, and syn and quote for syntactic processing and code generation.
Key Components
Structs and Attribute Parsing
ExternalDelegate
Represents an external type to which setter methods should be delegated.
Fields:
ty: Path— The external type to delegate to.field: Option<Ident>— Optional field of the struct to delegate through.method: Option<Ident>— Optional method to delegate through.prefix: Option<String>— Optional prefix added to delegated setter method names.
Derived from meta attributes via
darling::FromMeta.Usage: Allows generating setters that forward to fields or methods of other types.
ContainerAttrs
Parses attributes on the struct for configuring setter generation globally.
Supports attributes like
no_std,into,strip_option,borrow_self, assert_none,no_change_action, postprocess, result,bool,generate,generate_public,generate_private,prefix,generate_delegates, and trace.Uses
darling::FromDeriveInputand supports only named structs.Controls global behavior for all field setters unless overridden.
FieldAttrs
Parses attributes on individual struct fields.
Supports overrides for global settings:
rename,into,strip_option,borrow_self, assert_none,no_change_action, postprocess, result,bool,generate,skip, doc, trace.Allows fine-grained control per setter.
Internal Representation
ContainerDef
Internal struct representing a parsed and resolved configuration for the container (struct).
Holds the struct's name, type, generics, standard library choice (std or
core), flags, prefixes, delegate configurations, and tracing options.Created by
init_container_def().
FieldDef
Internal struct representing a parsed and resolved configuration for a single field's setter.
Holds the field's name, type, generated setter name, documentation tokens, and various flags and options.
Created by
init_field_def().
Functions
error(span: Span, data: &str) -> SynTokenStream
Generates a Rust compile error at the specified span with the given message.
Used to abort macro expansion with custom error messages.
init_container_def(input: &DeriveInput) -> Result<ContainerDef, SynTokenStream>
Parses the top-level attributes on the struct.
Determines whether to use std or
core.Sets defaults for generation flags and prefix.
Returns a fully initialized
ContainerDefor a compile error token stream.
init_field_def(container: &ContainerDef, field: &Field) -> Result<Option<FieldDef>, SynTokenStream>
Parses the attributes on a field.
Checks visibility and generation flags to decide if a setter should be generated.
Applies overrides and merges field-level settings with container-level defaults.
Returns None if the field should not generate a setter.
generate_setter_method(input: &DeriveInput, container: &ContainerDef, def: FieldDef, additional_prefix: &Option<String>, delegate_toks: &Option<SynTokenStream>) -> Result<SynTokenStream, SynTokenStream>
Generates the Rust code for a single setter method.
Parameters:
input: The parsed derive input (struct).container: Global container config.def: Field-specific config.additional_prefix: Optional prefix for the setter name (used for delegation).
delegate_toks: Optional token stream representing delegation target (field or method).
Handles:
Tracing calls (via the tracing crate).
Optional assertion that
Optionfields are None.Stripping
Option<T>to acceptTas input.Conversion via
Into.Boolean setters that set the field to
true.Handling ownership and borrowing of
self.Custom return types and post-processing expressions.
Delegation to other fields or methods.
Returns tokens for the setter method or an error if incompatible options are set.
generate_setters_for(input: &DeriveInput, data: &DataStruct, generics: &Generics, ty: SynTokenStream, additional_prefix: Option<String>, delegate_toks: Option<SynTokenStream>) -> Result<SynTokenStream, SynTokenStream>
Generates setters for all fields of a struct or a delegate target.
Uses
init_container_defandinit_field_defto gather config.Iterates over fields and invokes
generate_setter_method.Wraps the generated setters in an impl block with appropriate generics.
Supports adding prefixes and delegation tokens for delegated setters.
generate_setters(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream, TokenStream>
Orchestrates generation of setters for the main struct and any declared delegates.
Calls
generate_setters_forfor the struct and for eachExternalDelegate.Validates that delegates specify exactly one of
fieldormethod.Combines all generated tokens into a single token stream.
derive_setters(input: TokenStream) -> TokenStream
Entry point for the procedural macro
#[derive(Setters)].Parses input tokens into a
DeriveInput.Verifies the input is a struct.
Calls
generate_settersand returns generated tokens or compile errors.Emits an error if applied to non-struct types.
Implementation Details and Algorithms
Uses
darlingfor ergonomic meta attribute parsing with validation and defaults.Supports delegation patterns allowing setters to forward to fields or getter methods on other types.
Implements optional stripping of
Option<T>wrappers from setter parameters, allowing cleaner APIs.Integrates tracing calls conditionally, providing runtime logs of setter invocations with arguments.
Checks for no-op setter calls via
no_change_action, enabling custom behavior on unchanged values.Supports boolean setters that take no parameters and simply set the field to
true.Uses Rust's procedural macro features (
proc_macro2, syn,quote) for parsing and code generation.Carefully merges container-level and field-level options with precedence rules.
Enforces error conditions when incompatible options are set simultaneously (e.g., delegation with assert_none).
Generates setters with flexible ownership models for
self(by mutable reference or by value).Uses Rust's type system features like
impl Into<T>for flexible setter input types.
Interaction with Other Parts of the Application
This file defines a procedural macro that other crates or modules use by applying
#[derive(Setters)]on their structs.Generated setters become part of the struct's associated methods, enhancing ergonomics and reducing boilerplate.
Optional support for the tracing crate enables integration with runtime diagnostics and telemetry systems.
Supports delegation to external types, allowing composition and reuse of setter logic across different structs.
Uses the
darlingcrate for attribute parsing and validation.Relies on syn and
quotefor macro input parsing and code generation, which are standard in procedural macro development.The macro-generated code integrates seamlessly into the consuming crate's codebase as if handwritten.
Usage Examples
Basic Usage
#[derive(Setters)]
struct MyStruct {
field1: String,
field2: Option<i32>,
}
Generates setter methods
field1()andfield2()with default naming.Setters take ownership or mutable reference to self depending on config.
Supports
Intoconversions if enabled globally or per field.
Custom Prefix and Delegation
#[derive(Setters)]
#[setters(prefix = "set_")]
struct Config {
value: i64,
#[setters(skip)]
internal: String,
#[setters(rename = "custom_set")]
renamed_field: bool,
#[setters(generate_delegates(
ty = "OtherType",
field = "delegate_field",
prefix = "delegate_",
))]
delegate_field: DelegateType,
}
Setters will be prefixed with
set_.internalfield has no setter generated.renamed_fieldsetter is namedcustom_set.Delegated setters generated for
OtherTypethroughdelegate_fieldwithdelegate_prefix.
Visual Diagram
flowchart TD
A[derive_setters] --> B[generate_setters]
B --> C[init_container_def]
B --> D["generate_setters_for (main struct)"]
D --> E["init_field_def (for each field)"]
E --> F[generate_setter_method]
B --> G["generate_setters_for (delegates)"]
G --> H["generate_setter_method (with delegation)"]
derive_settersis the macro entry point.It calls
generate_setters, which initializes container config and generates setters for the main struct and each delegate.For each field,
init_field_defparses field attributes and decides if a setter is needed.generate_setter_methodcreates the actual setter code, handling options and delegation.
This file encapsulates the logic and structure to generate efficient, customizable setter methods for Rust structs, supporting complex attribute-driven configuration and delegation patterns. For further details on attribute macros and procedural macro development, refer to the relevant topics on Procedural Macros and Attribute Parsing.