oxihuman_core/
batch_processor.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct BatchProcessor {
9 batch_size: usize,
10 total_items: usize,
11 processed: usize,
12 failed: usize,
13 current_batch: usize,
14}
15
16#[allow(dead_code)]
17impl BatchProcessor {
18 pub fn new(batch_size: usize, total_items: usize) -> Self {
19 Self {
20 batch_size: batch_size.max(1),
21 total_items,
22 processed: 0,
23 failed: 0,
24 current_batch: 0,
25 }
26 }
27
28 pub fn batch_count(&self) -> usize {
29 if self.total_items == 0 {
30 return 0;
31 }
32 self.total_items.div_ceil(self.batch_size)
33 }
34
35 pub fn current_batch(&self) -> usize {
36 self.current_batch
37 }
38
39 pub fn batch_range(&self, batch_idx: usize) -> (usize, usize) {
40 let start = batch_idx * self.batch_size;
41 let end = (start + self.batch_size).min(self.total_items);
42 (start, end)
43 }
44
45 pub fn advance_batch(&mut self, success_count: usize, fail_count: usize) {
46 self.processed += success_count;
47 self.failed += fail_count;
48 self.current_batch += 1;
49 }
50
51 pub fn is_complete(&self) -> bool {
52 self.processed + self.failed >= self.total_items
53 }
54
55 pub fn progress(&self) -> f32 {
56 if self.total_items == 0 {
57 return 1.0;
58 }
59 (self.processed + self.failed) as f32 / self.total_items as f32
60 }
61
62 pub fn remaining(&self) -> usize {
63 self.total_items
64 .saturating_sub(self.processed + self.failed)
65 }
66
67 pub fn processed(&self) -> usize {
68 self.processed
69 }
70
71 pub fn failed(&self) -> usize {
72 self.failed
73 }
74
75 pub fn total_items(&self) -> usize {
76 self.total_items
77 }
78
79 pub fn batch_size(&self) -> usize {
80 self.batch_size
81 }
82
83 pub fn success_rate(&self) -> f32 {
84 let done = self.processed + self.failed;
85 if done == 0 {
86 return 1.0;
87 }
88 self.processed as f32 / done as f32
89 }
90
91 pub fn reset(&mut self) {
92 self.processed = 0;
93 self.failed = 0;
94 self.current_batch = 0;
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_new() {
104 let bp = BatchProcessor::new(10, 100);
105 assert_eq!(bp.batch_size(), 10);
106 assert_eq!(bp.total_items(), 100);
107 }
108
109 #[test]
110 fn test_batch_count() {
111 assert_eq!(BatchProcessor::new(10, 100).batch_count(), 10);
112 assert_eq!(BatchProcessor::new(10, 95).batch_count(), 10);
113 assert_eq!(BatchProcessor::new(10, 0).batch_count(), 0);
114 }
115
116 #[test]
117 fn test_batch_range() {
118 let bp = BatchProcessor::new(10, 25);
119 assert_eq!(bp.batch_range(0), (0, 10));
120 assert_eq!(bp.batch_range(2), (20, 25));
121 }
122
123 #[test]
124 fn test_advance_batch() {
125 let mut bp = BatchProcessor::new(10, 20);
126 bp.advance_batch(10, 0);
127 assert_eq!(bp.processed(), 10);
128 assert_eq!(bp.current_batch(), 1);
129 }
130
131 #[test]
132 fn test_is_complete() {
133 let mut bp = BatchProcessor::new(5, 10);
134 assert!(!bp.is_complete());
135 bp.advance_batch(5, 0);
136 bp.advance_batch(5, 0);
137 assert!(bp.is_complete());
138 }
139
140 #[test]
141 fn test_progress() {
142 let mut bp = BatchProcessor::new(5, 10);
143 bp.advance_batch(5, 0);
144 assert!((bp.progress() - 0.5).abs() < f32::EPSILON);
145 }
146
147 #[test]
148 fn test_remaining() {
149 let mut bp = BatchProcessor::new(5, 10);
150 bp.advance_batch(3, 2);
151 assert_eq!(bp.remaining(), 5);
152 }
153
154 #[test]
155 fn test_success_rate() {
156 let mut bp = BatchProcessor::new(10, 20);
157 bp.advance_batch(8, 2);
158 assert!((bp.success_rate() - 0.8).abs() < f32::EPSILON);
159 }
160
161 #[test]
162 fn test_reset() {
163 let mut bp = BatchProcessor::new(5, 10);
164 bp.advance_batch(5, 0);
165 bp.reset();
166 assert_eq!(bp.processed(), 0);
167 assert_eq!(bp.current_batch(), 0);
168 }
169
170 #[test]
171 fn test_empty_total() {
172 let bp = BatchProcessor::new(10, 0);
173 assert!(bp.is_complete());
174 assert!((bp.progress() - 1.0).abs() < f32::EPSILON);
175 }
176}