| Crates.io | component_model |
| lib.rs | component_model |
| version | 0.8.0 |
| created_at | 2025-04-20 06:20:23.202737+00 |
| updated_at | 2025-09-23 13:45:49.981951+00 |
| description | Revolutionary type-safe component assignment for Rust. Build complex objects with zero boilerplate using derive macros and type-driven field setting. Perfect for configuration builders, fluent APIs, and object composition patterns. |
| homepage | https://github.com/Wandalen/wTools/tree/master/module/core/component_model |
| repository | https://github.com/Wandalen/wTools/tree/master/module/core/component_model |
| max_upload_size | |
| id | 1641429 |
| size | 292,679 |
Revolutionary type-safe component assignment for Rust. Build complex objects with zero boilerplate using derive macros and type-driven field setting. Perfect for configuration builders, fluent APIs, and object composition patterns.
Traditional struct initialization is verbose and error-prone:
# struct Config { host : String, port : i32 }
# struct ConfigBuilder;
# impl ConfigBuilder
{
# fn new() -> Self { ConfigBuilder }
# fn host( self, _ : &str ) -> Self { self }
# fn port( self, _ : i32 ) -> Self { self }
# fn build( self ) -> Config { Config { host : "".to_string(), port : 0 } }
# }
// Traditional approach - repetitive and fragile
let config = Config
{
host : "localhost".to_string(),
port : 8080,
};
// Builder pattern - lots of boilerplate
let config = ConfigBuilder::new()
.host( "localhost" )
.port( 8080 )
.build();
Component Model approach - Clean, type-safe, zero boilerplate:
use component_model::Assign;
#[ derive( Default, Assign ) ]
struct Config
{
host : String,
port : i32,
}
// Set components by type - no field names needed!
let mut config = Config::default();
config.assign( "localhost" ); // Automatically sets String field
config.assign( 8080 ); // Automatically sets i32 field
// Or use fluent style
let config = Config::default()
.impute( "localhost" )
.impute( 8080 );
impute() method for builder patternsComponentsAssignAdd to your Cargo.toml:
[ dependencies ]
component_model = "0.4"
Component Model follows granular feature gating for minimal builds:
[ dependencies ]
# Minimal version - no features enabled by default
component_model = { version = "0.4", default-features = false }
# Enable specific features as needed
component_model = { version = "0.4", features = [ "derive_component_model" ] }
# Or enable all features (default)
component_model = { version = "0.4", features = [ "full" ] }
Available features:
enabled - Master switch for core functionalityfull - All features (enabled by default)derive_component_model - Unified ComponentModel derive macroderive_component_assign - Basic Assign derive macroderive_components_assign - Multiple component assignmentderive_component_from - Component creation from single valuesderive_from_components - Component creation from multiple valuesuse component_model::{ ComponentModel, Assign };
#[ derive( Default, Debug, ComponentModel ) ]
struct Person
{
age : i32,
name : String,
}
fn main()
{
let mut person = Person::default();
// Type-driven assignment - no field names!
person.assign( 25 ); // Sets age : i32
person.assign( "Alice" ); // Sets name : String
println!( "{:?}", person ); // Person { age: 25, name: "Alice" }
}
ComponentModel provides built-in support for popular Rust types with intelligent conversion:
use component_model::{ ComponentModel, Assign };
use std::time::Duration;
use std::path::PathBuf;
#[ derive( Default, Debug, ComponentModel ) ]
struct Config
{
timeout : Duration,
config_path : PathBuf,
port : i32,
}
fn main()
{
let mut config = Config::default();
// Duration from seconds (u64)
config.assign( 30u64 ); // Duration::from_secs( 30 )
// Duration from fractional seconds (f64)
config.assign( 2.5f64 ); // Duration::from_secs_f64( 2.5 )
// PathBuf from string slice
config.assign( "/etc/app.conf" ); // PathBuf::from( "/etc/app.conf" )
// i32 assignment
config.assign( 8080i32 );
}
ComponentModel works with structs that contain enum fields, enabling type-safe enum assignment:
use component_model::{ ComponentModel, Assign };
#[ derive( Debug, PartialEq ) ]
enum Status
{
Pending,
Processing { progress : f64 },
Completed { result : String },
Failed { error : String },
}
impl Default for Status
{
fn default() -> Self { Status::Pending }
}
#[ derive( Default, Debug, ComponentModel ) ]
struct Task
{
id : u32,
status : Status,
priority : u8,
}
fn main()
{
let mut task = Task::default();
// Use field-specific methods with enums
task.id_set( 42u32 );
task.priority_set( 5u8 );
task.status_set( Status::Processing { progress: 0.75 } );
println!( "{:?}", task );
// Fluent style with enums
let completed_task = Task::default()
.id_with( 100u32 )
.status_with( Status::Completed { result: "Success".to_string() } )
.priority_with( 1u8 );
match completed_task.status {
Status::Completed { result } => println!( "Task completed: {}", result ),
_ => println!( "Unexpected status" ),
}
}
use component_model::{ ComponentModel, Assign };
use std::time::Duration;
#[ derive( Debug ) ]
enum ConnectionState
{
Disconnected,
Connecting { timeout : Duration },
Connected { session_id : String },
}
impl Default for ConnectionState
{
fn default() -> Self { ConnectionState::Disconnected }
}
#[ derive( Default, Debug, ComponentModel ) ]
struct NetworkService
{
name : String,
state : ConnectionState,
retry_count : u32,
}
fn main()
{
let mut service = NetworkService::default();
// Field-specific methods work seamlessly with enum fields
service.name_set( "WebSocket".to_string() );
service.retry_count_set( 3u32 );
service.state_set( ConnectionState::Connected {
session_id: "sess_12345".to_string()
} );
// Fluent pattern with complex enums
let connecting_service = NetworkService::default()
.name_with( "HTTP Client".to_string() )
.state_with( ConnectionState::Connecting {
timeout: Duration::from_secs( 30 )
} )
.retry_count_with( 0u32 );
println!( "{:?}", connecting_service );
}
Note: Direct ComponentModel derive on enums is planned for future releases. Currently, enums work as field types in structs with ComponentModel.
# use component_model::{ ComponentModel, Assign };
# #[ derive( Default, ComponentModel ) ]
# struct Person { name : String, age : i32 }
let person = Person::default()
.impute( "Bob" ) // Chainable assignment
.impute( 30 ); // Returns Self for chaining
use component_model::{ ComponentModel, Assign };
#[ derive( Default, ComponentModel ) ]
struct ServerConfig
{
host : String,
port : i32,
}
let mut config = ServerConfig::default();
config.assign( "localhost" ); // String component
config.assign( 8080 ); // i32 component
For custom behavior, implement traits manually:
use component_model::prelude::*;
struct Database
{
url : String,
pool_size : usize,
}
impl< T : Into< String > > Assign< String, T > for Database
{
fn assign( &mut self, component : T )
{
self.url = component.into();
}
}
impl< T : Into< usize > > Assign< usize, T > for Database
{
fn assign( &mut self, component : T )
{
self.pool_size = component.into();
}
}
ComponentModel - โญ Recommended - Unified derive combining all functionalityAssign - Basic component assignment by typeComponentsAssign - Multiple component assignment from tuplesComponentFrom - Create objects from single componentsFromComponents - Create objects from multiple componentsuse component_model::{ ComponentModel, Assign };
use std::time::Duration;
use std::path::PathBuf;
#[ derive( Default, ComponentModel ) ]
struct DatabaseConfig
{
host : String,
port : i32,
timeout : Duration,
}
let config = DatabaseConfig::default()
.impute( "postgres.example.com" ) // String
.impute( 5432 ) // i32
.impute( 30u64 ); // Duration from seconds
use component_model::{ ComponentModel, Assign };
use std::time::Duration;
#[ derive( Default, ComponentModel ) ]
struct HttpClient
{
base_url : String,
timeout : Duration,
}
let client = HttpClient::default()
.impute( "https://api.example.com" )
.impute( 30.0f64 ); // Duration from fractional seconds
use component_model::{ ComponentModel, Assign };
#[ derive( Default, ComponentModel ) ]
struct Player
{
name : String,
level : i32,
}
// Initialize components
let mut player = Player::default();
player.assign( "Hero" );
player.assign( 1 );
Explore the examples directory for comprehensive usage patterns:
000_basic_assignment.rs - Basic component assignment001_fluent_builder.rs - Fluent builder pattern002_multiple_components.rs - Multiple component handling003_component_from.rs - Component creation patterns004_working_example.rs - Real-world usage scenarioscomponent_model_trivial.rs - Minimal exampleComponentModel includes built-in intelligent conversion for:
| Type | Input Types | Example |
|---|---|---|
Duration |
u64, f64, (u64, u32) |
config.assign( 30u64 ) |
PathBuf |
&str, String |
config.assign( "/path/file" ) |
SocketAddr |
Coming soon | String parsing planned |
HashMap |
Framework ready | Vec conversion planned |
HashSet |
Framework ready | Vec conversion planned |
Type Ambiguity: When a struct has multiple fields of the same type, assign() becomes ambiguous and won't compile. This is by design for type safety.
# use component_model::{ ComponentModel, Assign };
# #[ derive( Default, ComponentModel ) ]
struct Config
{
host : String,
database : String, // Multiple String fields cause ambiguity
}
// This won't compile due to ambiguity:
// let mut config = Config::default();
// config.assign( "localhost" ); // Error: which String field?
Workarounds:
String vs PathBuf)config.host = "localhost".to_string();Assign traits for specific use casesMade with โค๏ธ as part of the wTools ecosystem