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