1use crate::config::ComponentConfig;
45use core::any::Any;
46use core::fmt;
47use core::marker::PhantomData;
48use cu29_traits::{CuError, CuResult};
49
50use alloc::boxed::Box;
51use alloc::format;
52use alloc::sync::Arc;
53use alloc::vec::Vec;
54
55pub struct Owned<T>(pub T);
57
58pub struct Borrowed<'r, T>(pub &'r T);
61
62enum ResourceEntry {
64 Owned(Box<dyn Any + Send + Sync>),
65 Shared(Arc<dyn Any + Send + Sync>),
66}
67
68impl ResourceEntry {
69 fn as_shared<T: 'static + Send + Sync>(&self) -> Option<&T> {
70 match self {
71 ResourceEntry::Shared(arc) => arc.downcast_ref::<T>(),
72 ResourceEntry::Owned(boxed) => boxed.downcast_ref::<T>(),
73 }
74 }
75
76 fn as_shared_arc<T: 'static + Send + Sync>(&self) -> Option<Arc<T>> {
77 match self {
78 ResourceEntry::Shared(arc) => Arc::downcast::<T>(arc.clone()).ok(),
79 ResourceEntry::Owned(_) => None,
80 }
81 }
82
83 fn into_owned<T: 'static + Send + Sync>(self) -> Option<T> {
84 match self {
85 ResourceEntry::Owned(boxed) => boxed.downcast::<T>().map(|b| *b).ok(),
86 ResourceEntry::Shared(_) => None,
87 }
88 }
89}
90
91#[derive(Copy, Clone, Eq, PartialEq)]
93pub struct ResourceKey<T = ()> {
94 bundle: BundleIndex,
95 index: usize,
96 _boo: PhantomData<fn() -> T>,
97}
98
99impl<T> ResourceKey<T> {
100 pub const fn new(bundle: BundleIndex, index: usize) -> Self {
101 Self {
102 bundle,
103 index,
104 _boo: PhantomData,
105 }
106 }
107
108 pub const fn bundle(&self) -> BundleIndex {
109 self.bundle
110 }
111
112 pub const fn index(&self) -> usize {
113 self.index
114 }
115
116 pub fn typed<U>(self) -> ResourceKey<U> {
118 ResourceKey {
119 bundle: self.bundle,
120 index: self.index,
121 _boo: PhantomData,
122 }
123 }
124}
125
126impl<T> fmt::Debug for ResourceKey<T> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.debug_struct("ResourceKey")
129 .field("bundle", &self.bundle.index())
130 .field("index", &self.index)
131 .finish()
132 }
133}
134
135#[derive(Copy, Clone, Debug, Eq, PartialEq)]
137pub struct BundleIndex(usize);
138
139impl BundleIndex {
140 pub const fn new(index: usize) -> Self {
141 Self(index)
142 }
143
144 pub const fn index(self) -> usize {
145 self.0
146 }
147
148 pub fn key<T, I: ResourceId>(self, id: I) -> ResourceKey<T> {
149 ResourceKey::new(self, id.index())
150 }
151}
152
153pub trait ResourceId: Copy + Eq {
155 const COUNT: usize;
156 fn index(self) -> usize;
157}
158
159pub trait ResourceBundleDecl {
161 type Id: ResourceId;
162}
163
164pub trait NamedResourceBundleDecl: ResourceBundleDecl {
170 const NAMES: &'static [&'static str];
171}
172
173const fn str_eq(left: &str, right: &str) -> bool {
174 let left = left.as_bytes();
175 let right = right.as_bytes();
176 if left.len() != right.len() {
177 return false;
178 }
179
180 let mut idx = 0;
181 while idx < left.len() {
182 if left[idx] != right[idx] {
183 return false;
184 }
185 idx += 1;
186 }
187
188 true
189}
190
191#[doc(hidden)]
195pub const fn resource_index_by_name<B: NamedResourceBundleDecl>(name: &str) -> usize {
196 let mut idx = 0;
197 while idx < B::NAMES.len() {
198 if str_eq(B::NAMES[idx], name) {
199 return idx;
200 }
201 idx += 1;
202 }
203
204 panic!("resource slot name not declared by bundle");
205}
206
207#[derive(Clone, Copy)]
209pub struct ResourceBindingMap<B: Copy + Eq + 'static> {
210 entries: &'static [(B, ResourceKey)],
211}
212
213impl<B: Copy + Eq + 'static> ResourceBindingMap<B> {
214 pub const fn new(entries: &'static [(B, ResourceKey)]) -> Self {
215 Self { entries }
216 }
217
218 pub fn get(&self, binding: B) -> Option<ResourceKey> {
219 self.entries
220 .iter()
221 .find(|(entry_id, _)| *entry_id == binding)
222 .map(|(_, key)| *key)
223 }
224}
225
226pub struct ResourceManager {
228 bundles: Box<[BundleEntries]>,
229}
230
231struct BundleEntries {
232 entries: Box<[Option<ResourceEntry>]>,
233}
234
235impl ResourceManager {
236 pub fn new(bundle_sizes: &[usize]) -> Self {
239 let bundles = bundle_sizes
240 .iter()
241 .map(|size| {
242 let mut entries = Vec::with_capacity(*size);
243 entries.resize_with(*size, || None);
244 BundleEntries {
245 entries: entries.into_boxed_slice(),
246 }
247 })
248 .collect::<Vec<_>>();
249 Self {
250 bundles: bundles.into_boxed_slice(),
251 }
252 }
253
254 fn entry_mut<T>(&mut self, key: ResourceKey<T>) -> CuResult<&mut Option<ResourceEntry>> {
255 let bundle = self
256 .bundles
257 .get_mut(key.bundle.index())
258 .ok_or_else(|| CuError::from("Resource bundle index out of range"))?;
259 bundle
260 .entries
261 .get_mut(key.index)
262 .ok_or_else(|| CuError::from("Resource index out of range"))
263 }
264
265 fn entry<T>(&self, key: ResourceKey<T>) -> CuResult<&ResourceEntry> {
266 let bundle = self
267 .bundles
268 .get(key.bundle.index())
269 .ok_or_else(|| CuError::from("Resource bundle index out of range"))?;
270 bundle
271 .entries
272 .get(key.index)
273 .and_then(|opt| opt.as_ref())
274 .ok_or_else(|| CuError::from("Resource not found"))
275 }
276
277 fn take_entry<T>(&mut self, key: ResourceKey<T>) -> CuResult<ResourceEntry> {
278 let bundle = self
279 .bundles
280 .get_mut(key.bundle.index())
281 .ok_or_else(|| CuError::from("Resource bundle index out of range"))?;
282 let entry = bundle
283 .entries
284 .get_mut(key.index)
285 .and_then(|opt| opt.take())
286 .ok_or_else(|| CuError::from("Resource not found"))?;
287 Ok(entry)
288 }
289
290 pub fn add_owned<T: 'static + Send + Sync>(
292 &mut self,
293 key: ResourceKey<T>,
294 value: T,
295 ) -> CuResult<()> {
296 let entry = self.entry_mut(key)?;
297 if entry.is_some() {
298 return Err(CuError::from("Resource already registered"));
299 }
300 *entry = Some(ResourceEntry::Owned(Box::new(value)));
301 Ok(())
302 }
303
304 pub fn add_shared<T: 'static + Send + Sync>(
307 &mut self,
308 key: ResourceKey<T>,
309 value: Arc<T>,
310 ) -> CuResult<()> {
311 let entry = self.entry_mut(key)?;
312 if entry.is_some() {
313 return Err(CuError::from("Resource already registered"));
314 }
315 *entry = Some(ResourceEntry::Shared(value as Arc<dyn Any + Send + Sync>));
316 Ok(())
317 }
318
319 pub fn borrow<'r, T: 'static + Send + Sync>(
321 &'r self,
322 key: ResourceKey<T>,
323 ) -> CuResult<Borrowed<'r, T>> {
324 let entry = self.entry(key)?;
325 entry.as_shared::<T>().map(Borrowed).ok_or_else(|| {
326 CuError::from(format!(
327 "Borrowing Resource has unexpected type, expected '{}'",
328 core::any::type_name::<T>()
329 ))
330 })
331 }
332
333 pub fn borrow_shared_arc<T: 'static + Send + Sync>(
335 &self,
336 key: ResourceKey<T>,
337 ) -> CuResult<Arc<T>> {
338 let entry = self.entry(key)?;
339 entry.as_shared_arc::<T>().ok_or_else(|| {
340 CuError::from(format!(
341 "Borrow Shared Resource '{}' has unexpected type",
342 core::any::type_name::<T>()
343 ))
344 })
345 }
346
347 pub fn take<T: 'static + Send + Sync>(&mut self, key: ResourceKey<T>) -> CuResult<Owned<T>> {
349 let entry = self.take_entry(key)?;
350 entry.into_owned::<T>().map(Owned).ok_or_else(|| {
351 CuError::from(format!(
352 "Resource {} is not owned or has unexpected type",
353 core::any::type_name::<T>()
354 ))
355 })
356 }
357
358 pub fn add_bundle_prebuilt(
362 &mut self,
363 builder: impl FnOnce(&mut ResourceManager) -> CuResult<()>,
364 ) -> CuResult<()> {
365 builder(self)
366 }
367}
368
369pub trait ResourceBindings<'r>: Sized {
374 type Binding: Copy + Eq + 'static;
375
376 fn from_bindings(
377 manager: &'r mut ResourceManager,
378 mapping: Option<&ResourceBindingMap<Self::Binding>>,
379 ) -> CuResult<Self>;
380}
381
382impl<'r> ResourceBindings<'r> for () {
383 type Binding = ();
384
385 fn from_bindings(
386 _manager: &'r mut ResourceManager,
387 _mapping: Option<&ResourceBindingMap<Self::Binding>>,
388 ) -> CuResult<Self> {
389 Ok(())
390 }
391}
392
393pub trait ResourceBundle: ResourceBundleDecl + Sized {
396 fn build(
397 bundle: BundleContext<Self>,
398 config: Option<&ComponentConfig>,
399 manager: &mut ResourceManager,
400 ) -> CuResult<()>;
401}
402
403pub struct BundleContext<B: ResourceBundleDecl> {
405 bundle_index: BundleIndex,
406 bundle_id: &'static str,
407 _boo: PhantomData<B>,
408}
409
410impl<B: ResourceBundleDecl> BundleContext<B> {
411 pub const fn new(bundle_index: BundleIndex, bundle_id: &'static str) -> Self {
412 Self {
413 bundle_index,
414 bundle_id,
415 _boo: PhantomData,
416 }
417 }
418
419 pub const fn bundle_id(&self) -> &'static str {
420 self.bundle_id
421 }
422
423 pub const fn bundle_index(&self) -> BundleIndex {
424 self.bundle_index
425 }
426
427 pub fn key<T>(&self, id: B::Id) -> ResourceKey<T> {
428 ResourceKey::new(self.bundle_index, id.index())
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 #[derive(Copy, Clone, Eq, PartialEq)]
437 #[repr(usize)]
438 enum DummyBundleId {
439 Uart0,
440 I2c1,
441 }
442
443 impl ResourceId for DummyBundleId {
444 const COUNT: usize = 2;
445
446 fn index(self) -> usize {
447 self as usize
448 }
449 }
450
451 struct DummyBundle;
452
453 impl ResourceBundleDecl for DummyBundle {
454 type Id = DummyBundleId;
455 }
456
457 impl NamedResourceBundleDecl for DummyBundle {
458 const NAMES: &'static [&'static str] = &["uart0", "i2c1"];
459 }
460
461 #[test]
462 fn resource_index_by_name_matches_declared_slot_name() {
463 assert_eq!(DummyBundleId::Uart0.index(), 0);
464 assert_eq!(DummyBundleId::I2c1.index(), 1);
465 assert_eq!(resource_index_by_name::<DummyBundle>("uart0"), 0);
466 assert_eq!(resource_index_by_name::<DummyBundle>("i2c1"), 1);
467 }
468}