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
//! Aggregate reader decorator (#146).
//!
//! [`AggregatingReader`] combines up to a bounded number of delegate items
//! into one logical output item. The bound reuses [`crate::ChunkSize`] --
//! the framework's existing nonzero, validated item-count type -- rather than
//! introducing a second bounded-count type for the same purpose.
use crate::;
/// A reader decorator that aggregates up to a bounded number of delegate
/// items into one logical output item.
///
/// # Contract
///
/// - **Input/output**: `I -> O` via the supplied aggregation function, one
/// `O` per completed (or final partial) group of `I`.
/// - **State/checkpoint**: the in-flight buffer is in-memory only, exactly
/// like [`crate::item_components::PeekReader`]'s lookahead: it is never
/// reported to the framework as read progress until a full or final
/// partial group is returned from `read`, so it advances no checkpoint by
/// itself. A read failure preserves the buffer across the call (see
/// "Malformed input" below); only cooperative stop clears it, because a
/// stopped step attempt is not retried. A restart (a fresh process,
/// reconstructing this reader from scratch) resumes the delegate from its
/// own last committed position (via a paired [`crate::ItemStream`], if
/// any) and re-aggregates from there.
/// - **Restartability**: exactly the delegate's.
/// - **Ordering**: preserves delegate order within each aggregated group.
/// - **Thread safety**: used exclusively (`&mut self`) like every reader.
/// - **Bounded resource**: the buffer never exceeds `bound` items -- the
/// defining property of this component, proved by
/// [`crate::item_components`]'s aggregate tests, never left as the
/// default. There is no unbounded-accumulation mode.
/// - **Cancellation**: a delegate [`ReadOutcome::Stopped`] returns `Stopped`
/// immediately; any partially buffered group is discarded, not emitted.
/// - **Close**: nothing to close.
/// - **Malformed input**: propagates the delegate's [`ReaderError`]
/// unchanged without emitting a truncated aggregate. The in-flight buffer
/// is *not* cleared: the framework's fault-retry contract re-invokes this
/// same reader instance, from the same in-memory position, rather than
/// rewinding or reconstructing it ("replays the chunk from inputs it
/// already read, so a stateful reader never rewinds" -- see the facade's
/// fault-runtime documentation), so discarding already-accumulated
/// delegate items on a retryable failure would lose real, already-read
/// input. A retried call resumes accumulating from exactly where the
/// failed call left off; see
/// `aggregate_retry_after_failure_resumes_the_preserved_buffer` in the
/// evidence file below.
/// - **End of input**: a nonempty buffer at end of input is emitted once as
/// a final, possibly smaller-than-`bound` aggregate; the next call returns
/// [`ReadOutcome::EndOfInput`] and continues to do so.
/// - **Support tier**: first-party.
/// - **Evidence**: `crates/oxide-batch-test/tests/item_components_decorators.rs`.