#[component]Expand description
Component macro for declarative component registration
This macro allows you to register components to the summer-rs application container in a declarative way, without manually implementing the Plugin trait.
§Syntax
#[component]
fn create_component(
Config(config): Config<MyConfig>,
Component(dep): Component<Dependency>,
) -> MyComponent {
MyComponent::new(config, dep)
}
Declarative component registration macro for summer-rs applications.
The `#[component]` macro automatically generates a Plugin implementation that registers
a component in the application. It handles dependency injection, configuration loading,
and proper initialization order.
# How It Works
The macro transforms a component creation function into a Plugin that:
1. Extracts dependencies from function parameters
2. Generates a unique Plugin name based on the return type
3. Declares dependencies to ensure correct initialization order
4. Registers the component in the application registry
5. Automatically registers the Plugin via the `inventory` crate
# Syntax
```plain
#[component(name = "CustomName")] // Optional custom name
fn create_component(
Config(config): Config<ConfigType>, // Configuration injection
Component(dep): Component<DependencyType>, // Component injection
) -> ComponentType {
// Component creation logic
}§Attributes
name = "PluginName"- Optional: Custom Plugin name. If not specified, the name is automatically generated as__Create{TypeName}Plugin.
§Parameters
Config<T>- Inject configuration of typeT(must implementConfigurable)Component<T>- Inject another component of typeT- Can use
#[inject("PluginName")]attribute to specify explicit dependency - Without
#[inject], dependency is inferred as__Create{T}Plugin
- Can use
§Return Type
- Must implement
Clone + Send + Sync + 'static - Can return
Result<T, E>for fallible initialization (will panic on error) - Each component type can only be registered once
§Dependency Resolution
The macro automatically analyzes dependencies and generates a dependencies() method
that returns the list of required Plugin names. The application will initialize plugins
in the correct order based on these dependencies.
Circular dependencies are not allowed and will cause a panic at runtime.
§Examples
§Basic Usage
ⓘ
use summer::config::Configurable;
use summer::extractor::Config;
use summer_macros::component;
use serde::Deserialize;
#[derive(Clone, Configurable, Deserialize)]
#[config_prefix = "database"]
struct DbConfig {
host: String,
port: u16,
}
#[derive(Clone)]
struct DbConnection {
url: String,
}
#[component]
fn create_db_connection(
Config(config): Config<DbConfig>,
) -> DbConnection {
DbConnection {
url: format!("{}:{}", config.host, config.port),
}
}§With Dependencies
ⓘ
#[derive(Clone)]
struct UserRepository {
db: DbConnection,
}
#[component]
fn create_user_repository(
Component(db): Component<DbConnection>,
) -> UserRepository {
UserRepository { db }
}§Multi-Level Dependencies
ⓘ
#[derive(Clone)]
struct UserService {
repo: UserRepository,
}
#[component]
fn create_user_service(
Component(repo): Component<UserRepository>,
) -> UserService {
UserService { repo }
}§Async Initialization
ⓘ
#[component]
async fn create_db_connection(
Config(config): Config<DbConfig>,
) -> Result<DbConnection, anyhow::Error> {
let pool = sqlx::PgPool::connect(&config.url).await?;
Ok(DbConnection { pool })
}§Custom Plugin Name
Use custom names when you need multiple components of the same type (NewType pattern):
ⓘ
#[derive(Clone)]
struct PrimaryDb(DbConnection);
#[derive(Clone)]
struct SecondaryDb(DbConnection);
#[component(name = "PrimaryDatabase")]
fn create_primary_db(
Config(config): Config<PrimaryDbConfig>,
) -> PrimaryDb {
PrimaryDb(DbConnection::new(&config))
}
#[component(name = "SecondaryDatabase")]
fn create_secondary_db(
Config(config): Config<SecondaryDbConfig>,
) -> SecondaryDb {
SecondaryDb(DbConnection::new(&config))
}§Explicit Dependency
Use #[inject("PluginName")] when the dependency name cannot be inferred:
ⓘ
#[component]
fn create_repository(
#[inject("PrimaryDatabase")] Component(db): Component<PrimaryDb>,
) -> UserRepository {
UserRepository::new(db.0)
}§Usage in Application
Components defined with #[component] are automatically registered when the app is built:
ⓘ
use summer::App;
#[tokio::main]
async fn main() {
let app = App::new()
.build() // Auto plugins are registered automatically
.await
.expect("Failed to build app");
// Use components
let db = app.get_component::<DbConnection>().unwrap();
}§Best Practices
- Keep component functions simple - They should only create and configure the component
- Use NewType pattern for multiple instances - Wrap the same type in different structs
- Prefer configuration over hardcoding - Use
Config<T>for all configurable values - Use
Arc<T>for large components - Reduces clone overhead - Avoid circular dependencies - Refactor your design if you encounter them
- Use explicit names for clarity - When the auto-generated name is not clear enough
§Limitations
- Each component type can only be registered once (use NewType pattern for multiple instances)
- Circular dependencies are not supported
- Component types must implement
Clone + Send + Sync + 'static - Configuration types must implement
Configurable + Deserialize
§See Also
Config- Configuration injection wrapperComponent- Component injection wrapperConfigurable- Trait for configuration types