Skip to main content

oxihuman_core/
batch_queue.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Queue that drains items in fixed-size batches.
6
7use std::collections::VecDeque;
8
9/// A queue that yields items in batches of a configured size.
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct BatchQueue<T> {
13    items: VecDeque<T>,
14    batch_size: usize,
15}
16
17#[allow(dead_code)]
18impl<T> BatchQueue<T> {
19    pub fn new(batch_size: usize) -> Self {
20        let bs = if batch_size == 0 { 1 } else { batch_size };
21        Self {
22            items: VecDeque::new(),
23            batch_size: bs,
24        }
25    }
26
27    pub fn enqueue(&mut self, item: T) {
28        self.items.push_back(item);
29    }
30
31    pub fn enqueue_many(&mut self, iter: impl IntoIterator<Item = T>) {
32        for item in iter {
33            self.items.push_back(item);
34        }
35    }
36
37    pub fn drain_batch(&mut self) -> Vec<T> {
38        let n = self.batch_size.min(self.items.len());
39        self.items.drain(..n).collect()
40    }
41
42    pub fn has_full_batch(&self) -> bool {
43        self.items.len() >= self.batch_size
44    }
45
46    pub fn len(&self) -> usize {
47        self.items.len()
48    }
49
50    pub fn is_empty(&self) -> bool {
51        self.items.is_empty()
52    }
53
54    pub fn batch_size(&self) -> usize {
55        self.batch_size
56    }
57
58    pub fn pending_batch_count(&self) -> usize {
59        if self.items.is_empty() {
60            0
61        } else {
62            self.items.len().div_ceil(self.batch_size)
63        }
64    }
65
66    pub fn clear(&mut self) {
67        self.items.clear();
68    }
69
70    pub fn peek_front(&self) -> Option<&T> {
71        self.items.front()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn new_queue_is_empty() {
81        let q = BatchQueue::<i32>::new(4);
82        assert!(q.is_empty());
83    }
84
85    #[test]
86    fn enqueue_and_drain() {
87        let mut q = BatchQueue::new(2);
88        q.enqueue(1);
89        q.enqueue(2);
90        q.enqueue(3);
91        let batch = q.drain_batch();
92        assert_eq!(batch, vec![1, 2]);
93        assert_eq!(q.len(), 1);
94    }
95
96    #[test]
97    fn drain_partial_batch() {
98        let mut q = BatchQueue::new(5);
99        q.enqueue(10);
100        let batch = q.drain_batch();
101        assert_eq!(batch, vec![10]);
102    }
103
104    #[test]
105    fn has_full_batch() {
106        let mut q = BatchQueue::new(2);
107        q.enqueue(1);
108        assert!(!q.has_full_batch());
109        q.enqueue(2);
110        assert!(q.has_full_batch());
111    }
112
113    #[test]
114    fn pending_batch_count() {
115        let mut q = BatchQueue::new(3);
116        q.enqueue_many(0..7);
117        assert_eq!(q.pending_batch_count(), 3); // ceil(7/3)
118    }
119
120    #[test]
121    fn clear_empties() {
122        let mut q = BatchQueue::new(2);
123        q.enqueue(1);
124        q.clear();
125        assert!(q.is_empty());
126    }
127
128    #[test]
129    fn batch_size_zero_becomes_one() {
130        let q = BatchQueue::<i32>::new(0);
131        assert_eq!(q.batch_size(), 1);
132    }
133
134    #[test]
135    fn peek_front() {
136        let mut q = BatchQueue::new(4);
137        assert!(q.peek_front().is_none());
138        q.enqueue(42);
139        assert_eq!(q.peek_front(), Some(&42));
140    }
141
142    #[test]
143    fn enqueue_many_works() {
144        let mut q = BatchQueue::new(3);
145        q.enqueue_many(vec![1, 2, 3, 4]);
146        assert_eq!(q.len(), 4);
147    }
148
149    #[test]
150    fn drain_empty_returns_empty() {
151        let mut q = BatchQueue::<i32>::new(4);
152        assert!(q.drain_batch().is_empty());
153    }
154}