Skip to main content

oxihuman_core/
batch_processor.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// Processes items in configurable batch sizes with progress tracking.
6#[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}