1use crate::config::ComponentConfig;
40use core::any::Any;
41use core::fmt;
42use core::marker::PhantomData;
43use cu29_traits::{CuError, CuResult};
44
45use alloc::boxed::Box;
46use alloc::sync::Arc;
47use alloc::vec::Vec;
48
49pub struct Owned<T>(pub T);
51
52pub struct Borrowed<'r, T>(pub &'r T);
55
56enum ResourceEntry {
58 Owned(Box<dyn Any + Send + Sync>),
59 Shared(Arc<dyn Any + Send + Sync>),
60}
61
62impl ResourceEntry {
63 fn as_shared<T: 'static + Send + Sync>(&self) -> Option<&T> {
64 match self {
65 ResourceEntry::Shared(arc) => arc.downcast_ref::<T>(),
66 ResourceEntry::Owned(boxed) => boxed.downcast_ref::<T>(),
67 }
68 }
69
70 fn into_owned<T: 'static + Send + Sync>(self) -> Option<T> {
71 match self {
72 ResourceEntry::Owned(boxed) => boxed.downcast::<T>().map(|b| *b).ok(),
73 ResourceEntry::Shared(_) => None,
74 }
75 }
76}
77
78#[derive(Copy, Clone, Eq, PartialEq)]
80pub struct ResourceKey<T = ()> {
81 index: usize,
83 _boo: PhantomData<fn() -> T>,
84}
85
86impl<T> ResourceKey<T> {
87 pub const fn new(index: usize) -> Self {
88 Self {
89 index,
90 _boo: PhantomData,
91 }
92 }
93
94 pub const fn index(&self) -> usize {
95 self.index
96 }
97
98 pub fn typed<U>(self) -> ResourceKey<U> {
100 ResourceKey {
101 index: self.index,
102 _boo: PhantomData,
103 }
104 }
105}
106
107impl<T> fmt::Debug for ResourceKey<T> {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 f.debug_struct("ResourceKey")
110 .field("index", &self.index)
111 .finish()
112 }
113}
114
115#[derive(Copy, Clone, Debug)]
117pub struct ResourceDecl {
118 pub key: ResourceKey,
119 pub path: &'static str,
120}
121
122impl ResourceDecl {
123 pub const fn new(key: ResourceKey, path: &'static str) -> Self {
124 Self { key, path }
125 }
126}
127
128#[derive(Clone, Copy)]
131pub struct ResourceMapping {
132 entries: &'static [(&'static str, ResourceKey)],
133}
134
135impl ResourceMapping {
136 pub const fn new(entries: &'static [(&'static str, ResourceKey)]) -> Self {
137 Self { entries }
138 }
139
140 pub fn get(&self, name: &str) -> Option<ResourceKey> {
141 self.entries
142 .iter()
143 .find(|(entry_name, _)| *entry_name == name)
144 .map(|(_, key)| *key)
145 }
146}
147
148pub struct ResourceManager {
150 entries: Box<[Option<ResourceEntry>]>,
151}
152
153impl ResourceManager {
154 pub fn new(num_resources: usize) -> Self {
157 let mut entries = Vec::with_capacity(num_resources);
158 entries.resize_with(num_resources, || None);
159 Self {
160 entries: entries.into_boxed_slice(),
161 }
162 }
163
164 pub fn add_owned<T: 'static + Send + Sync>(
166 &mut self,
167 key: ResourceKey<T>,
168 value: T,
169 ) -> CuResult<()> {
170 let idx = key.index();
171 if self.entries[idx].is_some() {
172 return Err(CuError::from("Resource already registered"));
173 }
174 self.entries[idx] = Some(ResourceEntry::Owned(Box::new(value)));
175 Ok(())
176 }
177
178 pub fn add_shared<T: 'static + Send + Sync>(
181 &mut self,
182 key: ResourceKey<T>,
183 value: Arc<T>,
184 ) -> CuResult<()> {
185 let idx = key.index();
186 if self.entries[idx].is_some() {
187 return Err(CuError::from("Resource already registered"));
188 }
189 self.entries[idx] = Some(ResourceEntry::Shared(value as Arc<dyn Any + Send + Sync>));
190 Ok(())
191 }
192
193 pub fn borrow<'r, T: 'static + Send + Sync>(
195 &'r self,
196 key: ResourceKey<T>,
197 ) -> CuResult<Borrowed<'r, T>> {
198 let idx = key.index();
199 let entry = self
200 .entries
201 .get(idx)
202 .and_then(|opt| opt.as_ref())
203 .ok_or_else(|| CuError::from("Resource not found"))?;
204 entry
205 .as_shared::<T>()
206 .map(Borrowed)
207 .ok_or_else(|| CuError::from("Resource has unexpected type"))
208 }
209
210 #[cfg(feature = "std")]
212 pub fn borrow_shared_arc<T: 'static + Send + Sync>(
213 &self,
214 key: ResourceKey<T>,
215 ) -> CuResult<Arc<T>> {
216 let idx = key.index();
217 let entry = self
218 .entries
219 .get(idx)
220 .and_then(|opt| opt.as_ref())
221 .ok_or_else(|| CuError::from("Resource not found"))?;
222 match entry {
223 ResourceEntry::Shared(arc) => arc
224 .clone()
225 .downcast::<T>()
226 .map_err(|_| CuError::from("Resource has unexpected type")),
227 ResourceEntry::Owned(_) => Err(CuError::from("Resource is owned, not shared")),
228 }
229 }
230
231 pub fn take<T: 'static + Send + Sync>(&mut self, key: ResourceKey<T>) -> CuResult<Owned<T>> {
233 let idx = key.index();
234 let entry = self.entries.get_mut(idx).and_then(|opt| opt.take());
235 let entry = entry.ok_or_else(|| CuError::from("Resource not found"))?;
236 entry
237 .into_owned::<T>()
238 .map(Owned)
239 .ok_or_else(|| CuError::from("Resource is not owned or has unexpected type"))
240 }
241
242 pub fn add_bundle_prebuilt(
246 &mut self,
247 builder: impl FnOnce(&mut ResourceManager) -> CuResult<()>,
248 ) -> CuResult<()> {
249 builder(self)
250 }
251}
252
253pub trait ResourceBindings<'r>: Sized {
258 fn from_bindings(
259 manager: &'r mut ResourceManager,
260 mapping: Option<&ResourceMapping>,
261 ) -> CuResult<Self>;
262}
263
264impl<'r> ResourceBindings<'r> for () {
265 fn from_bindings(
266 _manager: &'r mut ResourceManager,
267 _mapping: Option<&ResourceMapping>,
268 ) -> CuResult<Self> {
269 Ok(())
270 }
271}
272
273pub trait ResourceBundle {
276 fn build(
277 bundle_id: &str,
278 config: Option<&ComponentConfig>,
279 resources: &[ResourceDecl],
280 manager: &mut ResourceManager,
281 ) -> CuResult<()>;
282}
283
284#[derive(Copy, Clone, Debug)]
289pub struct ResourceProvider {
290 pub id: &'static str,
291 pub provider_path: &'static str,
292 pub resources: &'static [ResourceDecl],
293}
294
295impl ResourceProvider {
296 pub const fn new(
297 id: &'static str,
298 provider_path: &'static str,
299 resources: &'static [ResourceDecl],
300 ) -> Self {
301 Self {
302 id,
303 provider_path,
304 resources,
305 }
306 }
307}
308
309#[cfg(feature = "std")]
313pub struct ThreadPoolBundle;
314
315#[cfg(feature = "std")]
316impl ResourceBundle for ThreadPoolBundle {
317 fn build(
318 bundle_id: &str,
319 config: Option<&ComponentConfig>,
320 resources: &[ResourceDecl],
321 manager: &mut ResourceManager,
322 ) -> CuResult<()> {
323 use rayon::ThreadPoolBuilder;
324
325 const DEFAULT_THREADS: usize = 2;
326 let threads: usize = config
327 .and_then(|cfg| cfg.get::<u64>("threads"))
328 .map(|v| v as usize)
329 .unwrap_or(DEFAULT_THREADS);
330
331 let pool = ThreadPoolBuilder::new()
332 .num_threads(threads)
333 .build()
334 .map_err(|e| CuError::from(format!("Failed to build threadpool: {e}")))?;
335
336 let key = resources
337 .iter()
338 .find(|decl| decl.path == format!("{bundle_id}.bg_threads"))
339 .ok_or_else(|| {
340 CuError::from("ThreadPoolBundle missing required resource 'bg_threads'")
341 })?
342 .key
343 .typed();
344
345 manager.add_shared(key, Arc::new(pool))
346 }
347}