component_model is the user-facing runtime crate for the component-based programming model, providing derive macros for type-safe component assignment. It aggregates component_model_meta (procedural macros) and component_model_types (trait definitions) into a unified API for building fluent APIs, configuration builders, and composable object systems.
Version: 0.12.0 Status: Experimental Category: Design Patterns (Absorption Pattern) Dependents: 0 workspace crates (external user-facing API)
Provide the unified user-facing API for component-based programming by aggregating procedural macros (component_model_meta) and trait definitions (component_model_types), enabling zero-boilerplate, type-safe component assignment for configuration builders and fluent APIs.
-
Derive Macro Re-exports
ComponentModel- Unified derive macro combining all functionalityAssign- Type-driven component assignment deriveComponentsAssign- Multiple component assignmentComponentFrom- Component creation from single valueFromComponents- Component creation from multiple values- All re-exported from
component_model_meta
-
Trait System Re-exports
Assign<T, IntoT>trait - Generic component assignmentOptionExt<T>trait - Option-aware assignmentAssignWithTypetrait - Explicit type assignmentPopularTypemarker - Standard library type support- All re-exported from
component_model_types
-
Absorption Pattern Implementation
- Runtime crate absorbs both meta and types crates
- Prevents circular dependencies in ecosystem
- Unified feature gating across all three crates
- Single import point for users
-
Popular Types Support
- Intelligent conversion for
Duration,PathBuf,SocketAddr, etc. - Built-in implementations for common std library types
- Exposed through
popular_typesmodule
- Intelligent conversion for
-
Feature Architecture
enabled: Master feature switchfull: All features (default)derive_component_model: Unified derive macroderive_component_assign: Basic Assign derivederive_components_assign: Multiple componentsderive_component_from: Single value constructionderive_from_components: Multiple value constructiontypes_component_assign: Trait system
-
No-std Support
no_stdfeature flaguse_allocfeature for allocation-dependent functionality- Propagates to component_model_types and collection_tools
-
Traditional Module Organization
- Standard namespaces: own, orphan, exposed, prelude
- Not using mod_interface! (absorption crate)
-
Dependency Module
- Explicit
dependencymodule re-exporting component_model_types and component_model_meta - Allows users to access underlying crates if needed
- Explicit
-
NOT Actual Implementation Code
- Does not contain procedural macro implementations (that's component_model_meta)
- Does not define traits (that's component_model_types)
- Rationale: Pure re-export/aggregation crate following absorption pattern
-
NOT Global Component Registry
- Does not provide global component registration
- Does not implement dependency injection container
- Rationale: Component model focuses on type-driven assignment, not DI
-
NOT Runtime Component Loading
- Does not load components at runtime
- Does not provide plugin system
- Rationale: Compile-time only derive macros
-
NOT Reflection System
- Does not discover components via reflection
- Does not provide runtime type inspection
- Rationale: Type-driven assignment is compile-time only
-
NOT Builder Pattern Implementation
- Does not implement builder pattern logic (that's former crate)
- Does not generate builder types
- Rationale: Component model provides assignment traits; former provides builder scaffolding
-
NOT Validation Framework
- Does not validate component assignments
- Does not enforce constraints
- Rationale: Validation is application-specific
-
NOT Object Composition Framework
- Does not provide composition utilities beyond assignment
- Does not implement component lifecycle
- Rationale: Focused on assignment, not lifecycle management
-
NOT Configuration Management
- Does not load/save configuration files
- Does not provide config parsing
- Rationale: Component model enables building config objects, not managing them
- component_model vs component_model_types: component_model re-exports types, types defines them
- component_model vs component_model_meta: component_model re-exports macros, meta implements them
- component_model vs former: component_model provides assignment traits, former provides builder pattern implementation
- Absorption pattern: Runtime crate absorbs meta and types to prevent circular dependencies
component_model (runtime, absorption crate)
├── Internal Dependencies
│ ├── component_model_meta (workspace, proc macros)
│ └── component_model_types (workspace, trait definitions)
└── Dev Dependencies
├── test_tools (workspace)
└── collection_tools (workspace, for tests)
component_model follows the absorption pattern with two absorbed crates:
Component Model Ecosystem
├── component_model_types (types only, no dependencies on others)
├── component_model_meta (proc macro, depends on types)
└── component_model (runtime, absorbs both meta and types)
Prevents Circular Dependencies:
- meta needs types for trait definitions
- runtime needs both meta (macros) and types (traits)
- No circular dependency possible
component_model
├── lib.rs (traditional namespaces, re-exports only)
├── dependency module (component_model_types, component_model_meta)
└── Standard namespaces: own, orphan, exposed, prelude
Note: Uses traditional module pattern, not mod_interface! (absorption crate)
enabled (master switch)
├── full (all features, default)
│ ├── derive_component_model (unified derive)
│ │ ├── derive_component_assign
│ │ ├── derive_components_assign
│ │ ├── derive_component_from
│ │ └── derive_from_components
│ ├── derive_components (alternative unified derive)
│ ├── derive_component_assign
│ ├── derive_components_assign
│ ├── derive_component_from
│ ├── derive_from_components
│ └── types_component_assign
│
no_std (embedded support)
└── use_alloc (requires alloc)
Default Features: enabled, full
Feature Propagation:
derive_*features propagate tocomponent_model_metatypes_*features propagate tocomponent_model_typesno_std/use_allocpropagate to both
#[cfg(feature = "derive_component_model")]
/// Unified derive macro combining all component model functionality
pub use component_model_meta::ComponentModel;
#[cfg(feature = "derive_component_assign")]
/// Derive Assign trait for type-driven component assignment
pub use component_model_meta::Assign;
#[cfg(feature = "derive_components_assign")]
/// Derive for assigning multiple components at once
pub use component_model_meta::ComponentsAssign;
#[cfg(feature = "derive_component_from")]
/// Derive for creating component from single value
pub use component_model_meta::ComponentFrom;
#[cfg(feature = "derive_from_components")]
/// Derive for creating component from multiple values
pub use component_model_meta::FromComponents;#[cfg(feature = "types_component_assign")]
pub use component_model_types::Assign;
pub use component_model_types::OptionExt;
pub use component_model_types::AssignWithType;
pub use component_model_types::PopularType;/// Popular type support for std library types
pub use component_model_types::popular_types;
/// Explicit dependency access
pub mod dependency {
pub use component_model_types;
pub use component_model_meta;
}use component_model::{ComponentModel, Assign};
#[derive(Default, Debug, ComponentModel)]
struct Person {
age: i32,
name: String,
}
let mut person = Person::default();
person.assign(25); // Sets age: i32
person.assign("Alice"); // Sets name: String
assert_eq!(person.age, 25);
assert_eq!(person.name, "Alice");use component_model::{ComponentModel, Assign};
#[derive(Default, ComponentModel)]
struct Config {
host: String,
port: i32,
}
// Fluent chaining
let config = Config::default()
.impute("localhost")
.impute(8080);
assert_eq!(config.host, "localhost");
assert_eq!(config.port, 8080);use component_model::{ComponentModel, Assign};
use std::time::Duration;
use std::path::PathBuf;
#[derive(Default, ComponentModel)]
struct AppConfig {
timeout: Duration,
config_path: PathBuf,
}
let mut config = AppConfig::default();
config.assign(Duration::from_secs(30));
config.assign(PathBuf::from("/etc/app.conf"));use component_model::{ComponentsAssign, Assign};
#[derive(Default, ComponentsAssign)]
struct Server {
host: String,
port: i32,
timeout: u64,
}
let mut server = Server::default();
server.components_assign(("localhost", 8080, 300u64));
assert_eq!(server.host, "localhost");
assert_eq!(server.port, 8080);
assert_eq!(server.timeout, 300);use component_model::FromComponents;
#[derive(FromComponents)]
struct Point {
x: i32,
y: i32,
}
let point = Point::from_components((10, 20));
assert_eq!(point.x, 10);
assert_eq!(point.y, 20);#![no_std]
extern crate alloc;
use component_model::{ComponentModel, Assign};
use alloc::string::String;
#[derive(Default, ComponentModel)]
struct Data {
value: i32,
label: String,
}
let mut data = Data::default();
data.assign(42);
data.assign("embedded");Internal:
component_model_meta(workspace, optional) - Procedural macro implementationscomponent_model_types(workspace, optional) - Trait definitions
Dev Dependencies:
test_tools(workspace) - Testing utilitiescollection_tools(workspace) - Collection macros for tests
component_model is a user-facing API crate designed for external consumption, not for use by other wTools crates. It serves as the entry point for the component model ecosystem.
Usage: Intended for application developers building:
- Configuration objects
- Fluent APIs
- Builder pattern implementations
- Composable systems
Problem: Circular dependency risk:
component_model (runtime)
↓ depends on
component_model_meta (proc macro)
↓ wants to depend on (for types)
component_model (runtime) ← CIRCULAR!
Solution: Three-crate architecture:
component_model_types (types only)
↑ ↑
component_model_meta component_model
(proc macro) (runtime, absorbs both)
Benefits:
- No Circular Dependencies: Meta depends on types, runtime depends on both
- User Simplicity: Single import (
use component_model::*) - Ecosystem Coherence: All parts versioned together
- Clear Separation: Types vs macros vs aggregation
component_model contains NO implementation code, only re-exports. This is intentional:
- Single Responsibility: Aggregation is its only job
- Simplicity: No logic = no bugs in aggregation layer
- Transparency: Users get exactly what meta/types provide
- Maintenance: Changes only in meta/types, not runtime
Tradeoff: Additional crate in workspace, but cleaner architecture.
The ComponentModel derive is a unified macro that combines:
Assign- Component assignmentComponentsAssign- Multiple componentsComponentFrom- Single value constructionFromComponents- Multiple value construction
Benefits:
- Simplicity: One derive for all functionality
- Discoverability: Users find one macro, get everything
- Consistency: Same macro across different use cases
Tradeoff: Larger generated code, but better UX.
Standard library types like Duration, PathBuf, SocketAddr need special handling because:
- Orphan Rule: Can't implement Assign for foreign types
- Common Patterns: These types appear frequently in configs
- User Expectation: Should "just work" for std types
Solution: Generate implementations in macro for recognized types.
Component model focuses on assignment, not validation:
- Single Responsibility: Assignment is orthogonal to validation
- Flexibility: Users can validate however they want
- Performance: No runtime overhead for validation
Validation should be added separately (e.g., using validator crates or custom logic).
component_model and former serve different purposes:
- component_model: Type-driven assignment mechanism
- former: Builder pattern scaffolding (type generation, end conditions, etc.)
former actually uses component_model internally for assignments, showing they're complementary.
- 43 test files: Comprehensive behavior validation
- Doc Tests: Embedded in readme.md
- Integration Tests: Uses test_tools for integration testing
- Derive Macro Correctness: Verify generated code works
- Popular Types: Verify std library type support
- Feature Gating: Verify each feature works independently
- No-std: Verify functionality in embedded environments
- Type Safety: Verify compile-time type checking
- Standalone Constructors: Generate top-level constructor functions
- Argument Attributes: Mark fields as constructor arguments
- Enum Support: Component model for enum variants
- Custom Error Types: Better error reporting in derives
- More Popular Types: Expand std library type support
- Unified API: Consolidate all derives into ComponentModel only
- Feature Structure: Simplify feature hierarchy
- Decouple from former: Already decoupled, maintain separation
- Orphan Rule: Cannot implement Assign for foreign types without macro
- Type Ambiguity: Multiple fields of same type require workarounds
- No Validation: Assignment doesn't validate component values
Good Candidates:
- Configuration objects with many fields
- Fluent API implementations
- Builder pattern with type-driven assignment
- Applications needing flexible object composition
Poor Candidates:
- Simple structs with few fields (use direct initialization)
- Performance-critical code (trait overhead)
- Foreign types (orphan rule limitations)
// Before: Manual builder
impl MyStruct {
fn new() -> Self { Default::default() }
fn with_field1(mut self, val: T1) -> Self { self.field1 = val; self }
fn with_field2(mut self, val: T2) -> Self { self.field2 = val; self }
}
// After: ComponentModel
#[derive(Default, ComponentModel)]
struct MyStruct {
field1: T1,
field2: T2,
}
// Methods generated automatically!- Use ComponentModel Derive: Prefer unified derive over individual derives
- Derive Default: Always derive Default for fluent APIs
- Document Fields: Component model works best with well-documented types
- Avoid Type Ambiguity: Multiple fields of same type need special handling
- component_model_types: Type definitions (absorbed crate)
- component_model_meta: Procedural macros (absorbed crate)
- former: Builder pattern implementation (uses component_model internally)
- collection_tools: Collection macros (used in tests)
- API Documentation
- Repository
- component_model_types - Trait definitions
- component_model_meta - Procedural macros
- readme.md