Skip to content

Commit 6242ec4

Browse files
committed
wip
1 parent 5e99371 commit 6242ec4

File tree

2 files changed

+49
-304
lines changed

2 files changed

+49
-304
lines changed
Lines changed: 45 additions & 304 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
<!-- {{# generate.module_header{} #}} -->
22

33
# Module :: component_model
4-
<!--{ generate.module_header.start() }-->
5-
[![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_component_model_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_component_model_push.yml) [![docs.rs](https://img.shields.io/docsrs/component_model?color=e3e8f0&logo=docs.rs)](https://docs.rs/component_model) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcomponent_model%2Fexamples%2Fcomponent_model_trivial.rs,RUN_POSTFIX=--example%20module%2Fcore%2Fcomponent_model%2Fexamples%2Fcomponent_model_trivial.rs/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY)
6-
<!--{ generate.module_header.end }-->
74

8-
A flexible implementation of the Builder pattern supporting nested builders and collection-specific subcomponent_models.
5+
[![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental)
6+
[![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_component_model_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_component_model_push.yml)
7+
[![docs.rs](https://img.shields.io/docsrs/component_model?color=e3e8f0&logo=docs.rs)](https://docs.rs/component_model)
8+
[![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcomponent_model%2Fexamples%2Fcomponent_model_trivial.rs,RUN_POSTFIX=--example%20module%2Fcore%2Fcomponent_model%2Fexamples%2Fcomponent_model_trivial.rs/https://github.com/Wandalen/wTools)
9+
[![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY)
910

10-
## What is `Former`?
11-
12-
The `component_model` crate provides a powerful derive macro, `#[ derive( Former ) ]`, that automatically implements the **Builder pattern** for your Rust structs and enums.
13-
14-
Its primary goal is to **simplify the construction of complex objects**, especially those with numerous fields, optional values, default settings, collections, or nested structures, making your initialization code more readable and maintainable.
15-
16-
## Why Use `Former`?
17-
18-
Compared to manually implementing the Builder pattern or using other builder crates, `component_model` offers several advantages:
19-
20-
* **Reduced Boilerplate:** `#[ derive( Former ) ]` automatically generates the builder struct, storage, and setters, saving you significant repetitive coding effort.
21-
* **Fluent & Readable API:** Construct objects step-by-step using clear, chainable methods (`.field_name( value )`).
22-
* **Effortless Defaults & Optionals:** Fields automatically use their `Default` implementation if not set. `Option< T >` fields are handled seamlessly – you only set them if you have a `Some( value )`. Custom defaults can be specified easily with `#[ component_model( default = ... ) ]`.
23-
* **Powerful Collection & Nested Struct Handling:** `component_model` truly shines with its **subcomponent_model** system. Easily build `Vec`, `HashMap`, `HashSet`, and other collections element-by-element, or configure nested structs using their own dedicated component_models within the parent's builder chain. This is often more complex to achieve with other solutions.
11+
A flexible component model for Rust supporting generic assignment and type-based field access.
2412

2513
## Installation
2614

@@ -30,300 +18,53 @@ Add `component_model` to your `Cargo.toml`:
3018
cargo add component_model
3119
```
3220

33-
The default features enable the `Former` derive macro and support for standard collections, covering most common use cases.
34-
35-
## Basic Usage
36-
37-
Derive `Former` on your struct and use the generated `::component_model()` method to start building:
21+
## Minimal Example: Using Assign
3822

3923
```rust
40-
# #[ cfg( any( not( feature = "derive_component_model" ), not( feature = "enabled" ) ) ) ]
41-
# fn main() {}
42-
# #[ cfg( all( feature = "derive_component_model", feature = "enabled" ) ) ]
43-
# fn main()
44-
# {
45-
use component_model::Former;
46-
47-
#[ derive( Debug, PartialEq, Former ) ]
48-
pub struct UserProfile
49-
{
50-
age : i32, // Required field
51-
username : String, // Required field
52-
bio : Option< String >, // Optional field
24+
use component_model::prelude::Assign;
25+
26+
#[derive(Debug, PartialEq, Default)]
27+
struct Person {
28+
age: i32,
29+
name: String,
30+
}
31+
32+
impl<IntoT> Assign<i32, IntoT> for Person
33+
where
34+
IntoT: Into<i32>,
35+
{
36+
fn assign(&mut self, component: IntoT) {
37+
self.age = component.into();
5338
}
54-
55-
let profile = UserProfile::component_model()
56-
.age( 30 )
57-
.username( "JohnDoe".to_string() )
58-
// .bio is optional, so we don't *have* to call its setter
59-
.form();
60-
61-
let expected = UserProfile
62-
{
63-
age : 30,
64-
username : "JohnDoe".to_string(),
65-
bio : None, // Defaults to None if not set
66-
};
67-
assert_eq!( profile, expected );
68-
dbg!( &profile );
69-
// > &profile = UserProfile {
70-
// > age: 30,
71-
// > username: "JohnDoe",
72-
// > bio: None,
73-
// > }
74-
75-
// Example setting the optional field:
76-
let profile_with_bio = UserProfile::component_model()
77-
.age( 30 )
78-
.username( "JohnDoe".to_string() )
79-
.bio( "Software Developer".to_string() ) // Set the optional bio
80-
.form();
81-
82-
let expected_with_bio = UserProfile
83-
{
84-
age : 30,
85-
username : "JohnDoe".to_string(),
86-
bio : Some( "Software Developer".to_string() ),
87-
};
88-
assert_eq!( profile_with_bio, expected_with_bio );
89-
dbg!( &profile_with_bio );
90-
// > &profile_with_bio = UserProfile {
91-
// > age: 30,
92-
// > username: "JohnDoe",
93-
// > bio: Some( "Software Developer" ),
94-
// > }
95-
# }
96-
```
97-
98-
[Run this example locally](https://github.com/Wandalen/wTools/blob/master/module/core/component_model/examples/component_model_trivial.rs) | [Try it online](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcomponent_model%2Fexamples%2Fcomponent_model_trivial.rs,RUN_POSTFIX=--example%20module%2Fcore%2Fcomponent_model%2Fexamples%2Fcomponent_model_trivial.rs/https://github.com/Wandalen/wTools)
99-
100-
## Handling Optionals and Defaults
101-
102-
`Former` makes working with optional fields and default values straightforward:
103-
104-
* **`Option< T >` Fields:** As seen in the basic example, fields of type `Option< T >` automatically default to `None`. You only need to call the setter if you have a `Some( value )`.
105-
106-
* **Custom Defaults:** For required fields that don't implement `Default`, or when you need a specific default value other than the type's default, use the `#[ component_model( default = ... ) ]` attribute:
107-
108-
```rust
109-
# #[ cfg( any( not( feature = "derive_component_model" ), not( feature = "enabled" ) ) ) ]
110-
# fn main() {}
111-
# #[ cfg( all( feature = "derive_component_model", feature = "enabled" ) ) ]
112-
# fn main()
113-
# {
114-
use component_model::Former;
115-
116-
#[ derive( Debug, PartialEq, Former ) ]
117-
pub struct Config
118-
{
119-
#[ component_model( default = 1024 ) ] // Use 1024 if .buffer_size() is not called
120-
buffer_size : i32,
121-
timeout : Option< i32 >, // Defaults to None
122-
#[ component_model( default = true ) ] // Default for bool
123-
enabled : bool,
39+
}
40+
41+
impl<IntoT> Assign<String, IntoT> for Person
42+
where
43+
IntoT: Into<String>,
44+
{
45+
fn assign(&mut self, component: IntoT) {
46+
self.name = component.into();
12447
}
125-
126-
// Only set the optional timeout
127-
let config1 = Config::component_model()
128-
.timeout( 5000 )
129-
.form();
130-
131-
assert_eq!( config1.buffer_size, 1024 ); // Got default
132-
assert_eq!( config1.timeout, Some( 5000 ) );
133-
assert_eq!( config1.enabled, true ); // Got default
134-
135-
// Set everything, overriding defaults
136-
let config2 = Config::component_model()
137-
.buffer_size( 4096 )
138-
.timeout( 1000 )
139-
.enabled( false )
140-
.form();
141-
142-
assert_eq!( config2.buffer_size, 4096 );
143-
assert_eq!( config2.timeout, Some( 1000 ) );
144-
assert_eq!( config2.enabled, false );
145-
# }
48+
}
49+
50+
fn main() {
51+
let mut person = Person::default();
52+
person.assign(42);
53+
person.assign("Alice");
54+
assert_eq!(person, Person { age: 42, name: "Alice".to_string() });
55+
}
14656
```
147-
[See full example code](https://github.com/Wandalen/wTools/blob/master/module/core/component_model/examples/component_model_custom_defaults.rs)
148-
149-
## Building Collections & Nested Structs (Subcomponent_models)
150-
151-
Where `component_model` significantly simplifies complex scenarios is in building collections (`Vec`, `HashMap`, etc.) or nested structs. It achieves this through **subcomponent_models**. Instead of setting the entire collection/struct at once, you get a dedicated builder for the field:
152-
153-
**Example: Building a `Vec`**
154-
155-
```rust
156-
# #[ cfg( not( all( feature = "enabled", feature = "derive_component_model", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
157-
# fn main() {}
158-
# #[ cfg( all( feature = "enabled", feature = "derive_component_model", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
159-
# fn main()
160-
# {
161-
use component_model::Former;
162-
163-
#[ derive( Debug, PartialEq, Former ) ]
164-
pub struct Report
165-
{
166-
title : String,
167-
#[ subform_collection ] // Enables the `.entries()` subcomponent_model
168-
entries : Vec< String >,
169-
}
170-
171-
let report = Report::component_model()
172-
.title( "Log Report".to_string() )
173-
.entries() // Get the subcomponent_model for the Vec
174-
.add( "Entry 1".to_string() ) // Use subcomponent_model methods to modify the Vec
175-
.add( "Entry 2".to_string() )
176-
.end() // Return control to the parent component_model (ReportFormer)
177-
.form(); // Finalize the Report
178-
179-
assert_eq!( report.title, "Log Report" );
180-
assert_eq!( report.entries, vec![ "Entry 1".to_string(), "Entry 2".to_string() ] );
181-
dbg!( &report );
182-
// > &report = Report {
183-
// > title: "Log Report",
184-
// > entries: [
185-
// > "Entry 1",
186-
// > "Entry 2",
187-
// > ],
188-
// > }
189-
# }
190-
```
191-
[See Vec example](https://github.com/Wandalen/wTools/blob/master/module/core/component_model/examples/component_model_collection_vector.rs) | [See HashMap example](https://github.com/Wandalen/wTools/blob/master/module/core/component_model/examples/component_model_collection_hashmap.rs)
192-
193-
`component_model` provides different subform attributes (`#[ subform_collection ]`, `#[ subform_entry ]`, `#[ subform_scalar ]`) for various collection and nesting patterns.
194-
195-
## Standalone Constructors
196-
197-
For scenarios where you want a direct constructor function instead of always starting with `YourType::component_model()`, `component_model` offers standalone constructors.
198-
199-
* **Enable:** Add `#[ standalone_constructors ]` to your struct or enum definition.
200-
* **Function Name:** A function named after your type (in snake_case) will be generated (e.g., `my_struct()` for `struct MyStruct`). For enums, functions are named after variants (e.g., `my_variant()` for `enum E { MyVariant }`).
201-
* **Arguments:** By default, the constructor takes no arguments and returns the `Former` type.
202-
* **Specify Arguments:** Mark specific fields with `#[ arg_for_constructor ]` to make them required arguments for the standalone constructor.
203-
* **Return Type (Option 2 Logic):**
204-
* If **all** fields of the struct/variant are marked with `#[ arg_for_constructor ]`, the standalone constructor returns the instance directly (`Self`).
205-
* If **zero or some** fields are marked, the standalone constructor returns the `Former` type, pre-initialized with the provided arguments.
206-
207-
**Example: Struct Standalone Constructors**
208-
209-
```rust
210-
# #[ cfg( any( not( feature = "derive_component_model" ), not( feature = "enabled" ) ) ) ]
211-
# fn main() {}
212-
# #[ cfg( all( feature = "derive_component_model", feature = "enabled" ) ) ]
213-
# fn main()
214-
# {
215-
use component_model::Former;
216-
217-
#[ derive( Debug, PartialEq, Former ) ]
218-
#[ standalone_constructors ] // Enable standalone constructors
219-
pub struct ServerConfig
220-
{
221-
#[ arg_for_constructor ] // This field is a constructor arg
222-
host : String,
223-
#[ arg_for_constructor ] // This field is also a constructor arg
224-
port : u16,
225-
timeout : Option< u32 >, // This field is NOT a constructor arg
226-
}
227-
228-
// Not all fields are args, so `server_config` returns the Former
229-
let config_component_model = server_config( "localhost".to_string(), 8080u16 ); // Added u16 suffix
230-
231-
// Set the remaining field and form
232-
let config = config_component_model
233-
.timeout( 5000u32 ) // Added u32 suffix
234-
.form();
235-
236-
assert_eq!( config.host, "localhost" );
237-
assert_eq!( config.port, 8080u16 ); // Added u16 suffix
238-
assert_eq!( config.timeout, Some( 5000u32 ) ); // Added u32 suffix
239-
240-
#[ derive( Debug, PartialEq, Former ) ]
241-
#[ standalone_constructors ]
242-
pub struct Point
243-
{
244-
#[ arg_for_constructor ]
245-
x : i32,
246-
#[ arg_for_constructor ]
247-
y : i32,
248-
}
249-
250-
// ALL fields are args, so `point` returns Self directly
251-
let p = point( 10, 20 );
252-
assert_eq!( p.x, 10 );
253-
assert_eq!( p.y, 20 );
254-
# }
255-
```
256-
257-
**Example: Enum Standalone Constructors**
258-
259-
<!-- qqq : xxx : fix it -->
260-
<!-- ```rust
261-
# #[ cfg( any( not( feature = "derive_component_model" ), not( feature = "enabled" ) ) ) ]
262-
# fn main() {}
263-
# #[ cfg( all( feature = "derive_component_model", feature = "enabled" ) ) ]
264-
# fn main()
265-
# {
266-
use component_model::Former;
267-
268-
#[ derive( Debug, PartialEq, Former ) ]
269-
#[ standalone_constructors ]
270-
pub enum Message
271-
{
272-
Quit, // Unit variant constructor `quit()` returns Self
273-
Write // Tuple variant constructor `write()` returns Former
274-
{
275-
#[ arg_for_constructor ] // Only this field is an arg
276-
text : String,
277-
urgent : bool, // Not an arg
278-
},
279-
Move // Struct variant constructor `move_point()` returns Self
280-
{
281-
#[ arg_for_constructor ]
282-
x : i32,
283-
#[ arg_for_constructor ]
284-
y : i32,
285-
}
286-
}
287-
288-
// Unit variant - returns Self
289-
let m1 = quit();
290-
assert_eq!( m1, Message::Quit );
291-
292-
// Tuple variant - not all fields are args, returns Former
293-
let m2_component_model = write( "hello".to_string() );
294-
let m2 = m2_component_model.urgent( true ).form();
295-
assert_eq!( m2, Message::Write { text: "hello".to_string(), urgent: true } );
29657

297-
// Struct variant - all fields are args, returns Self
298-
let m3 = r#move( 1, 2 ); // Use raw identifier `r#move` as `move` is a keyword
299-
assert_eq!( m3, Message::Move { x: 1, y: 2 } );
300-
# }
301-
``` -->
58+
## API Overview
30259

303-
## Key Features Overview
60+
- **Assign**: Generic trait for assigning values to struct fields by type.
61+
- **AssignWithType**: Trait for assigning values with explicit type annotation.
62+
- **ComponentsAssign**: Trait for assigning multiple components at once.
30463

305-
* **Automatic Builder Generation:** `#[ derive( Former ) ]` for structs and enums.
306-
* **Fluent API:** Chainable setter methods for a clean construction flow.
307-
* **Defaults & Optionals:** Seamless handling of `Default` values and `Option< T >` fields. Custom defaults via `#[ component_model( default = ... ) ]`.
308-
* **Subcomponent_models:** Powerful mechanism for building nested structures and collections:
309-
* `#[ subform_scalar ]`: For fields whose type also derives `Former`.
310-
* `#[ subform_collection ]`: For collections like `Vec`, `HashMap`, `HashSet`, etc., providing methods like `.add()` or `.insert()`.
311-
* `#[ subform_entry ]`: For collections where each entry is built individually using its own component_model.
312-
* **Customization:**
313-
* Rename setters: `#[ scalar( name = ... ) ]`, `#[ subform_... ( name = ... ) ]`.
314-
* Disable default setters: `#[ scalar( setter = false ) ]`, `#[ subform_... ( setter = false ) ]`.
315-
* Define custom setters directly in `impl Former`.
316-
* Specify collection definitions: `#[ subform_collection( definition = ... ) ]`.
317-
* **Advanced Control:**
318-
* Storage-only fields: `#[ storage_fields( ... ) ]`.
319-
* Custom mutation logic: `#[ mutator( custom ) ]` + `impl FormerMutator`.
320-
* Custom end-of-forming logic: Implement `FormingEnd`.
321-
* Custom collection support: Implement `Collection` traits.
322-
* **Component Model:** Separate derives (`Assign`, `ComponentFrom`, `ComponentsAssign`, `FromComponents`) for type-based field access and conversion (See `component_model_types` documentation).
64+
See [component_model_types documentation](https://docs.rs/component_model_types) for details.
32365

32466
## Where to Go Next
32567

326-
* **[Advanced Usage & Concepts](https://github.com/Wandalen/wTools/tree/master/module/core/component_model/advanced.md):** Dive deeper into subcomponent_models, customization options, storage, context, definitions, mutators, and custom collections.
327-
* **[Examples Directory](https://github.com/Wandalen/wTools/tree/master/module/core/component_model/examples):** Explore practical, runnable examples showcasing various features.
328-
* **[API Documentation (docs.rs)](https://docs.rs/component_model):** Get detailed information on all public types, traits, and functions.
329-
* **[Repository (GitHub)](https://github.com/Wandalen/wTools/tree/master/module/core/component_model):** View the source code, contribute, or report issues.
68+
- [Examples Directory](https://github.com/Wandalen/wTools/tree/master/module/core/component_model/examples): Explore practical, runnable examples.
69+
- [API Documentation (docs.rs)](https://docs.rs/component_model): Get detailed information on all public types, traits, and functions.
70+
- [Repository (GitHub)](https://github.com/Wandalen/wTools/tree/master/module/core/component_model): View the source code, contribute, or report issues.

module/core/component_model/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ pub mod dependency
2626
#[ cfg( feature = "enabled" ) ]
2727
pub use own::*;
2828

29+
#[ allow( unused_imports ) ]
30+
#[ cfg( feature = "enabled" ) ]
31+
// Former macro is intentionally not re-exported; all coupling with "former" is removed.
32+
2933
/// Own namespace of the module.
3034
#[ cfg( feature = "enabled" ) ]
3135
#[ allow( unused_imports ) ]

0 commit comments

Comments
 (0)