1use crate::config::ComponentConfig;
41use core::any::Any;
42use core::fmt;
43use core::marker::PhantomData;
44use cu29_traits::{CuError, CuResult};
45
46use alloc::boxed::Box;
47use alloc::format;
48use alloc::sync::Arc;
49use alloc::vec::Vec;
50
51pub struct Owned<T>(pub T);
53
54pub struct Borrowed<'r, T>(pub &'r T);
57
58enum ResourceEntry {
60 Owned(Box<dyn Any + Send + Sync>),
61 Shared(Arc<dyn Any + Send + Sync>),
62}
63
64impl ResourceEntry {
65 fn as_shared<T: 'static + Send + Sync>(&self) -> Option<&T> {
66 match self {
67 ResourceEntry::Shared(arc) => arc.downcast_ref::<T>(),
68 ResourceEntry::Owned(boxed) => boxed.downcast_ref::<T>(),
69 }
70 }
71
72 #[cfg(feature = "std")]
73 fn as_shared_arc<T: 'static + Send + Sync>(&self) -> Option<Arc<T>> {
74 match self {
75 ResourceEntry::Shared(arc) => Arc::downcast::<T>(arc.clone()).ok(),
76 ResourceEntry::Owned(_) => None,
77 }
78 }
79
80 fn into_owned<T: 'static + Send + Sync>(self) -> Option<T> {
81 match self {
82 ResourceEntry::Owned(boxed) => boxed.downcast::<T>().map(|b| *b).ok(),
83 ResourceEntry::Shared(_) => None,
84 }
85 }
86}
87
88#[derive(Copy, Clone, Eq, PartialEq)]
90pub struct ResourceKey<T = ()> {
91 bundle: BundleIndex,
92 index: usize,
93 _boo: PhantomData<fn() -> T>,
94}
95
96impl<T> ResourceKey<T> {
97 pub const fn new(bundle: BundleIndex, index: usize) -> Self {
98 Self {
99 bundle,
100 index,
101 _boo: PhantomData,
102 }
103 }
104
105 pub const fn bundle(&self) -> BundleIndex {
106 self.bundle
107 }
108
109 pub const fn index(&self) -> usize {
110 self.index
111 }
112
113 pub fn typed<U>(self) -> ResourceKey<U> {
115 ResourceKey {
116 bundle: self.bundle,
117 index: self.index,
118 _boo: PhantomData,
119 }
120 }
121}
122
123impl<T> fmt::Debug for ResourceKey<T> {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 f.debug_struct("ResourceKey")
126 .field("bundle", &self.bundle.index())
127 .field("index", &self.index)
128 .finish()
129 }
130}
131
132#[derive(Copy, Clone, Debug, Eq, PartialEq)]
134pub struct BundleIndex(usize);
135
136impl BundleIndex {
137 pub const fn new(index: usize) -> Self {
138 Self(index)
139 }
140
141 pub const fn index(self) -> usize {
142 self.0
143 }
144
145 pub fn key<T, I: ResourceId>(self, id: I) -> ResourceKey<T> {
146 ResourceKey::new(self, id.index())
147 }
148}
149
150pub trait ResourceId: Copy + Eq {
152 const COUNT: usize;
153 fn index(self) -> usize;
154}
155
156pub trait ResourceBundleDecl {
158 type Id: ResourceId;
159}
160
161#[derive(Clone, Copy)]
163pub struct ResourceBindingMap<B: Copy + Eq + 'static> {
164 entries: &'static [(B, ResourceKey)],
165}
166
167impl<B: Copy + Eq + 'static> ResourceBindingMap<B> {
168 pub const fn new(entries: &'static [(B, ResourceKey)]) -> Self {
169 Self { entries }
170 }
171
172 pub fn get(&self, binding: B) -> Option<ResourceKey> {
173 self.entries
174 .iter()
175 .find(|(entry_id, _)| *entry_id == binding)
176 .map(|(_, key)| *key)
177 }
178}
179
180pub struct ResourceManager {
182 bundles: Box<[BundleEntries]>,
183}
184
185struct BundleEntries {
186 entries: Box<[Option<ResourceEntry>]>,
187}
188
189impl ResourceManager {
190 pub fn new(bundle_sizes: &[usize]) -> Self {
193 let bundles = bundle_sizes
194 .iter()
195 .map(|size| {
196 let mut entries = Vec::with_capacity(*size);
197 entries.resize_with(*size, || None);
198 BundleEntries {
199 entries: entries.into_boxed_slice(),
200 }
201 })
202 .collect::<Vec<_>>();
203 Self {
204 bundles: bundles.into_boxed_slice(),
205 }
206 }
207
208 fn entry_mut<T>(&mut self, key: ResourceKey<T>) -> CuResult<&mut Option<ResourceEntry>> {
209 let bundle = self
210 .bundles
211 .get_mut(key.bundle.index())
212 .ok_or_else(|| CuError::from("Resource bundle index out of range"))?;
213 bundle
214 .entries
215 .get_mut(key.index)
216 .ok_or_else(|| CuError::from("Resource index out of range"))
217 }
218
219 fn entry<T>(&self, key: ResourceKey<T>) -> CuResult<&ResourceEntry> {
220 let bundle = self
221 .bundles
222 .get(key.bundle.index())
223 .ok_or_else(|| CuError::from("Resource bundle index out of range"))?;
224 bundle
225 .entries
226 .get(key.index)
227 .and_then(|opt| opt.as_ref())
228 .ok_or_else(|| CuError::from("Resource not found"))
229 }
230
231 fn take_entry<T>(&mut self, key: ResourceKey<T>) -> CuResult<ResourceEntry> {
232 let bundle = self
233 .bundles
234 .get_mut(key.bundle.index())
235 .ok_or_else(|| CuError::from("Resource bundle index out of range"))?;
236 let entry = bundle
237 .entries
238 .get_mut(key.index)
239 .and_then(|opt| opt.take())
240 .ok_or_else(|| CuError::from("Resource not found"))?;
241 Ok(entry)
242 }
243
244 pub fn add_owned<T: 'static + Send + Sync>(
246 &mut self,
247 key: ResourceKey<T>,
248 value: T,
249 ) -> CuResult<()> {
250 let entry = self.entry_mut(key)?;
251 if entry.is_some() {
252 return Err(CuError::from("Resource already registered"));
253 }
254 *entry = Some(ResourceEntry::Owned(Box::new(value)));
255 Ok(())
256 }
257
258 pub fn add_shared<T: 'static + Send + Sync>(
261 &mut self,
262 key: ResourceKey<T>,
263 value: Arc<T>,
264 ) -> CuResult<()> {
265 let entry = self.entry_mut(key)?;
266 if entry.is_some() {
267 return Err(CuError::from("Resource already registered"));
268 }
269 *entry = Some(ResourceEntry::Shared(value as Arc<dyn Any + Send + Sync>));
270 Ok(())
271 }
272
273 pub fn borrow<'r, T: 'static + Send + Sync>(
275 &'r self,
276 key: ResourceKey<T>,
277 ) -> CuResult<Borrowed<'r, T>> {
278 let entry = self.entry(key)?;
279 entry.as_shared::<T>().map(Borrowed).ok_or_else(|| {
280 CuError::from(format!(
281 "Borrowing Resource has unexpected type, expected '{}'",
282 core::any::type_name::<T>()
283 ))
284 })
285 }
286
287 #[cfg(feature = "std")]
289 pub fn borrow_shared_arc<T: 'static + Send + Sync>(
290 &self,
291 key: ResourceKey<T>,
292 ) -> CuResult<Arc<T>> {
293 let entry = self.entry(key)?;
294 entry.as_shared_arc::<T>().ok_or_else(|| {
295 CuError::from(format!(
296 "Borrow Shared Resource '{}' has unexpected type",
297 core::any::type_name::<T>()
298 ))
299 })
300 }
301
302 pub fn take<T: 'static + Send + Sync>(&mut self, key: ResourceKey<T>) -> CuResult<Owned<T>> {
304 let entry = self.take_entry(key)?;
305 entry.into_owned::<T>().map(Owned).ok_or_else(|| {
306 CuError::from(format!(
307 "Resource {} is not owned or has unexpected type",
308 core::any::type_name::<T>()
309 ))
310 })
311 }
312
313 pub fn add_bundle_prebuilt(
317 &mut self,
318 builder: impl FnOnce(&mut ResourceManager) -> CuResult<()>,
319 ) -> CuResult<()> {
320 builder(self)
321 }
322}
323
324pub trait ResourceBindings<'r>: Sized {
329 type Binding: Copy + Eq + 'static;
330
331 fn from_bindings(
332 manager: &'r mut ResourceManager,
333 mapping: Option<&ResourceBindingMap<Self::Binding>>,
334 ) -> CuResult<Self>;
335}
336
337impl<'r> ResourceBindings<'r> for () {
338 type Binding = ();
339
340 fn from_bindings(
341 _manager: &'r mut ResourceManager,
342 _mapping: Option<&ResourceBindingMap<Self::Binding>>,
343 ) -> CuResult<Self> {
344 Ok(())
345 }
346}
347
348pub trait ResourceBundle: ResourceBundleDecl + Sized {
351 fn build(
352 bundle: BundleContext<Self>,
353 config: Option<&ComponentConfig>,
354 manager: &mut ResourceManager,
355 ) -> CuResult<()>;
356}
357
358pub struct BundleContext<B: ResourceBundleDecl> {
360 bundle_index: BundleIndex,
361 bundle_id: &'static str,
362 _boo: PhantomData<B>,
363}
364
365impl<B: ResourceBundleDecl> BundleContext<B> {
366 pub const fn new(bundle_index: BundleIndex, bundle_id: &'static str) -> Self {
367 Self {
368 bundle_index,
369 bundle_id,
370 _boo: PhantomData,
371 }
372 }
373
374 pub const fn bundle_id(&self) -> &'static str {
375 self.bundle_id
376 }
377
378 pub const fn bundle_index(&self) -> BundleIndex {
379 self.bundle_index
380 }
381
382 pub fn key<T>(&self, id: B::Id) -> ResourceKey<T> {
383 ResourceKey::new(self.bundle_index, id.index())
384 }
385}
386
387#[cfg(feature = "std")]
388pub struct ThreadPoolBundle;
389
390#[cfg(feature = "std")]
391#[derive(Copy, Clone, Debug, Eq, PartialEq)]
392#[repr(usize)]
393pub enum ThreadPoolId {
394 BgThreads,
395}
396
397#[cfg(feature = "std")]
398impl ResourceId for ThreadPoolId {
399 const COUNT: usize = 1;
400
401 fn index(self) -> usize {
402 self as usize
403 }
404}
405
406#[cfg(feature = "std")]
407impl ResourceBundleDecl for ThreadPoolBundle {
408 type Id = ThreadPoolId;
409}
410
411#[cfg(feature = "std")]
412impl ResourceBundle for ThreadPoolBundle {
413 fn build(
414 bundle: BundleContext<Self>,
415 config: Option<&ComponentConfig>,
416 manager: &mut ResourceManager,
417 ) -> CuResult<()> {
418 use rayon::ThreadPoolBuilder;
419
420 const DEFAULT_THREADS: usize = 2;
421 let threads: usize = match config {
422 Some(cfg) => cfg
423 .get::<u64>("threads")?
424 .map(|v| v as usize)
425 .unwrap_or(DEFAULT_THREADS),
426 None => DEFAULT_THREADS,
427 };
428
429 let pool = ThreadPoolBuilder::new()
430 .num_threads(threads)
431 .build()
432 .map_err(|e| CuError::from(format!("Failed to build threadpool: {e}")))?;
433
434 let key = bundle.key::<rayon::ThreadPool>(ThreadPoolId::BgThreads);
435 manager.add_shared(key, Arc::new(pool))?;
436 Ok(())
437 }
438}