macro_rules! reducer_compose {
($state:expr, $action:expr, { $($arms:tt)+ }) => { ... };
($state:expr, $action:expr, $context:expr, { $($arms:tt)+ }) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) category $category:expr => $handler:expr, $($rest:tt)+) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) context $context_value:expr => $handler:expr, $($rest:tt)+) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) _ => $handler:expr, $($rest:tt)+) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) $pattern:pat $(if $guard:expr)? => $handler:expr, $($rest:tt)+) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) category $category:expr => $handler:expr $(,)?) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) context $context_value:expr => $handler:expr $(,)?) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) _ => $handler:expr $(,)?) => { ... };
(@accum $state:ident, $action:ident, $context:ident; ($($out:tt)*) $pattern:pat $(if $guard:expr)? => $handler:expr $(,)?) => { ... };
}Expand description
Compose a reducer by routing actions to focused handlers.
§When to Use
For most reducers, a flat match is simpler and clearer. Use this macro when:
- Your reducer exceeds ~500 lines and splitting improves organization
- You have context-aware routing (e.g., vim normal vs command mode)
- Handlers live in separate modules and you want clean composition
§Syntax
reducer_compose!(state, action, {
// Arms are tried in order, first match wins
category "name" => handler, // Route by action category
Action::Specific => handler, // Route by pattern match
_ => fallback_handler, // Catch-all (required last)
})
// With context (e.g., for modal/mode-aware routing):
reducer_compose!(state, action, context, {
context Mode::Command => handle_command, // Route by context value
category "nav" => handle_nav,
_ => handle_default,
})§Arm Types
category "name" => handler - Routes actions where
ActionCategory::category(&action) == Some("name"). Requires
#[action(infer_categories)] on your action enum.
context Value => handler - Routes when the context expression equals
Value. Only available in the 4-argument form.
Pattern => handler - Standard pattern match (e.g., Action::Quit,
Action::Input(_)).
_ => handler - Catch-all fallback. Must be last.
§Handler Signature
All handlers must have the same signature:
fn handler(state: &mut S, action: A) -> RWhere R is typically bool or DispatchResult<E>.
§Category Inference
With #[action(infer_categories)], categories are inferred from action
names by taking the prefix before the verb:
| Action | Verb | Category |
|---|---|---|
NavScrollUp | Scroll | "nav" |
SearchQuerySubmit | Submit | "search_query" |
WeatherDidLoad | Load | "weather_did" |
Quit | (none) | None |
For predictable categories, use explicit #[category = "name"] attributes.
§Example
fn reducer(state: &mut AppState, action: Action, mode: Mode) -> bool {
reducer_compose!(state, action, mode, {
// Command mode gets priority
context Mode::Command => handle_command,
// Then route by category
category "nav" => handle_navigation,
category "search" => handle_search,
// Specific actions
Action::Quit => |_, _| false,
// Everything else
_ => handle_ui,
})
}
fn handle_navigation(state: &mut AppState, action: Action) -> bool {
match action {
Action::NavUp => { state.cursor -= 1; true }
Action::NavDown => { state.cursor += 1; true }
_ => false,
}
}