1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//! Classifier-selected delegate components (#146).
//!
//! A classifier picks one delegate from a bounded, configured set at runtime,
//! keyed by a value derived from the item. Every delegate is stored under one
//! generic delegate type `D`: when delegates are naturally homogeneous
//! (multiple configurations of the same component type), `D` stays
//! monomorphized and the typed hot path is preserved; when delegates are
//! genuinely heterogeneous (different concrete types), naming `D` as
//! [`crate::BoxedProcessor`]/[`crate::BoxedWriter`] erases them all to the
//! *same* type at construction time, at the existing accepted erasure
//! boundary.
//!
//! # Why this cannot overclaim a delegate's capability
//!
//! Because every entry in the delegate map shares one Rust type `D`, the
//! wrapper's static declaration (its `Send`/`Sync`/lifetime bounds) is a
//! property of `D` itself, not of whichever key a given call happens to
//! select. A heterogeneous set is only representable by erasing every
//! variant to `Boxed*` first, which imposes the same (weakest) capability
//! uniformly on all of them; there is no way to construct a
//! [`ClassifyingProcessor`] or [`ClassifyingWriter`] whose static capability
//! is stronger than its least-capable delegate, because the type system
//! never sees "the selected delegate" as a distinct type from "every
//! delegate this wrapper could select" -- both are `D`.
//!
//! Delegate errors and stops propagate unchanged: neither type reclassifies,
//! swallows, or discriminates on which delegate produced them.
use HashMap;
use Hash;
use PhantomData;
use crate::;
/// Derives a delegate-selection key from a borrowed item.
///
/// Blanket-implemented for `Fn(&I) -> K + Send + Sync` closures.
/// Routes each item to one of a bounded, configured set of delegate
/// processors, selected at runtime by [`Classifier`].
///
/// # Contract
///
/// - **Input/output**: `I -> O`, same as every delegate `D`.
/// - **State/checkpoint**: stateless (assuming delegates are).
/// - **Ordering**: preserves order (one item in, one outcome out, exactly
/// like any processor).
/// - **Thread safety**: `Send + Sync` whenever `D` and `C` are; identical for
/// every key, because every delegate shares type `D` (see module docs).
/// - **Reentrancy**: fully reentrant when every delegate is.
/// - **Transaction/delivery**: not applicable.
/// - **Bounded resource**: the delegate set itself is bounded at
/// construction; this type adds no per-item buffering.
/// - **Cancellation**: honors the call-scoped stop token before classifying.
/// - **Close**: nothing to close.
/// - **Malformed input/failure**: an item whose key has no registered
/// delegate is a typed [`ProcessorError`]; a selected delegate's own
/// failure is returned unchanged, never reclassified.
/// - **Support tier**: first-party.
/// - **Evidence**: `crates/oxide-batch-test/tests/item_components_classify.rs`.
/// Routes each item in a written batch to one of a bounded, configured set of
/// delegate writers, selected at runtime by [`Classifier`].
///
/// Each item is written to its selected delegate as its own single-item
/// batch, in the batch's original order: this preserves exact input ordering
/// across delegates (rather than grouping same-key items into larger
/// sub-batches) and needs no `Clone` bound on `O`. When enlisted, the single
/// `&mut dyn BusinessTransaction` is reborrowed sequentially per item exactly
/// as [`crate::item_components::FanOutWriter`] does.
///
/// # Contract
///
/// - **Input/output**: `[O]`, same as every delegate `D`.
/// - **State/checkpoint**: stateless.
/// - **Ordering**: preserves the batch's original item order exactly (see
/// above); order-sensitive if any delegate is.
/// - **Thread safety**: `Send + Sync` whenever `D` and `C` are, identically
/// for every key (see module docs).
/// - **Reentrancy**: fully reentrant when every delegate is.
/// - **Transaction/delivery**: never claims a stronger mode than every
/// delegate supports; see the reborrow guarantee above. Requires `O: Sync`
/// because each borrowed item is held across its delegate's `await`.
/// - **Bounded resource**: the delegate set is bounded at construction.
/// - **Cancellation**: checked once before the first item, and again via each
/// delegate's own outcome.
/// - **Close**: nothing to close.
/// - **Malformed input/failure**: an item whose key has no registered
/// delegate is a typed [`WriterError`]; a selected delegate's own failure
/// is returned unchanged.
/// - **Support tier**: first-party.
/// - **Evidence**: `crates/oxide-batch-test/tests/item_components_classify.rs`.