Skip to main content

oxihuman_core/
resource_pool.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Resource pool with borrowing semantics.
6
7/// State of a pooled resource.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum ResourceState {
10    Available,
11    InUse,
12}
13
14/// A single resource slot.
15#[derive(Debug)]
16pub struct ResourceSlot<T> {
17    pub resource: T,
18    pub state: ResourceState,
19    pub borrow_count: u32,
20}
21
22/// Pool of resources with acquire/release semantics.
23pub struct ResourcePool<T> {
24    slots: Vec<ResourceSlot<T>>,
25}
26
27#[allow(dead_code)]
28impl<T: Clone> ResourcePool<T> {
29    pub fn new(resources: Vec<T>) -> Self {
30        let slots = resources
31            .into_iter()
32            .map(|r| ResourceSlot {
33                resource: r,
34                state: ResourceState::Available,
35                borrow_count: 0,
36            })
37            .collect();
38        ResourcePool { slots }
39    }
40
41    pub fn acquire(&mut self) -> Option<usize> {
42        self.slots.iter_mut().enumerate().find_map(|(i, s)| {
43            if s.state == ResourceState::Available {
44                s.state = ResourceState::InUse;
45                s.borrow_count += 1;
46                Some(i)
47            } else {
48                None
49            }
50        })
51    }
52
53    pub fn release(&mut self, index: usize) -> bool {
54        if index >= self.slots.len() {
55            return false;
56        }
57        if self.slots[index].state == ResourceState::InUse {
58            self.slots[index].state = ResourceState::Available;
59            true
60        } else {
61            false
62        }
63    }
64
65    pub fn get(&self, index: usize) -> Option<&T> {
66        self.slots.get(index).map(|s| &s.resource)
67    }
68
69    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
70        self.slots.get_mut(index).map(|s| &mut s.resource)
71    }
72
73    pub fn is_in_use(&self, index: usize) -> bool {
74        self.slots
75            .get(index)
76            .is_some_and(|s| s.state == ResourceState::InUse)
77    }
78
79    pub fn available_count(&self) -> usize {
80        self.slots
81            .iter()
82            .filter(|s| s.state == ResourceState::Available)
83            .count()
84    }
85
86    pub fn in_use_count(&self) -> usize {
87        self.slots
88            .iter()
89            .filter(|s| s.state == ResourceState::InUse)
90            .count()
91    }
92
93    pub fn total_count(&self) -> usize {
94        self.slots.len()
95    }
96
97    pub fn borrow_count(&self, index: usize) -> u32 {
98        self.slots.get(index).map(|s| s.borrow_count).unwrap_or(0)
99    }
100
101    pub fn release_all(&mut self) {
102        for s in &mut self.slots {
103            s.state = ResourceState::Available;
104        }
105    }
106
107    pub fn total_borrows(&self) -> u64 {
108        self.slots.iter().map(|s| s.borrow_count as u64).sum()
109    }
110}
111
112pub fn new_resource_pool<T: Clone>(resources: Vec<T>) -> ResourcePool<T> {
113    ResourcePool::new(resources)
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn acquire_available() {
122        let mut pool = new_resource_pool(vec![10i32, 20, 30]);
123        let idx = pool.acquire().expect("should succeed");
124        assert!(pool.is_in_use(idx));
125    }
126
127    #[test]
128    fn release_makes_available() {
129        let mut pool = new_resource_pool(vec![1i32]);
130        let idx = pool.acquire().expect("should succeed");
131        assert!(pool.release(idx));
132        assert!(!pool.is_in_use(idx));
133    }
134
135    #[test]
136    fn acquire_all_then_none() {
137        let mut pool = new_resource_pool(vec![1i32, 2]);
138        pool.acquire().expect("should succeed");
139        pool.acquire().expect("should succeed");
140        assert!(pool.acquire().is_none());
141    }
142
143    #[test]
144    fn available_count() {
145        let mut pool = new_resource_pool(vec![1i32, 2, 3]);
146        pool.acquire();
147        assert_eq!(pool.available_count(), 2);
148        assert_eq!(pool.in_use_count(), 1);
149    }
150
151    #[test]
152    fn get_resource() {
153        let pool = new_resource_pool(vec![42i32]);
154        assert_eq!(*pool.get(0).expect("should succeed"), 42);
155    }
156
157    #[test]
158    fn borrow_count_tracking() {
159        let mut pool = new_resource_pool(vec![1i32]);
160        pool.acquire();
161        pool.release(0);
162        pool.acquire();
163        assert_eq!(pool.borrow_count(0), 2);
164    }
165
166    #[test]
167    fn release_all() {
168        let mut pool = new_resource_pool(vec![1i32, 2, 3]);
169        pool.acquire();
170        pool.acquire();
171        pool.release_all();
172        assert_eq!(pool.available_count(), 3);
173    }
174
175    #[test]
176    fn total_borrows() {
177        let mut pool = new_resource_pool(vec![1i32, 2]);
178        pool.acquire();
179        pool.release(0);
180        pool.acquire();
181        pool.acquire();
182        assert_eq!(pool.total_borrows(), 3);
183    }
184
185    #[test]
186    fn out_of_bounds_release() {
187        let mut pool = new_resource_pool(vec![1i32]);
188        assert!(!pool.release(99));
189    }
190}