1use crate::error::{Error, Result};
8use std::any::Any;
9use std::collections::HashMap;
10use std::collections::HashSet;
11use std::fmt::{Debug, Formatter};
12use std::sync::Arc;
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash)]
16pub struct ResourceId(String);
17
18impl ResourceId {
19 pub fn new(id: impl Into<String>) -> Result<Self> {
21 let id = id.into();
22
23 if id.trim().is_empty() {
24 return Err(Error::InvalidArgument("resource id cannot be empty"));
25 }
26
27 Ok(Self(id))
28 }
29
30 pub fn as_str(&self) -> &str {
32 &self.0
33 }
34}
35
36impl From<ResourceId> for String {
37 fn from(value: ResourceId) -> Self {
38 value.0
39 }
40}
41
42impl AsRef<str> for ResourceId {
43 fn as_ref(&self) -> &str {
44 self.as_str()
45 }
46}
47
48#[derive(Clone)]
50pub enum Resource {
51 Audio(AudioBuffer),
53 F32(Arc<[f32]>),
55 F64(Arc<[f64]>),
57 Bytes(Arc<[u8]>),
59 Text(Arc<str>),
61 Any(Arc<dyn Any + Send + Sync>),
63}
64
65impl Debug for Resource {
66 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
67 match self {
68 Self::Audio(buffer) => f
69 .debug_struct("Audio")
70 .field("sample_rate", &buffer.sample_rate)
71 .field("samples", &buffer.samples.len())
72 .finish(),
73 Self::F32(data) => f.debug_tuple("F32").field(&data.len()).finish(),
74 Self::F64(data) => f.debug_tuple("F64").field(&data.len()).finish(),
75 Self::Bytes(data) => f.debug_tuple("Bytes").field(&data.len()).finish(),
76 Self::Text(data) => f.debug_tuple("Text").field(&data.len()).finish(),
77 Self::Any(_) => f.write_str("Any(<opaque>)"),
78 }
79 }
80}
81
82impl Resource {
83 pub fn f32(data: impl Into<Arc<[f32]>>) -> Self {
85 Self::F32(data.into())
86 }
87
88 pub fn audio(buffer: AudioBuffer) -> Self {
90 Self::Audio(buffer)
91 }
92
93 pub fn f64(data: impl Into<Arc<[f64]>>) -> Self {
95 Self::F64(data.into())
96 }
97
98 pub fn bytes(data: impl Into<Arc<[u8]>>) -> Self {
100 Self::Bytes(data.into())
101 }
102
103 pub fn text(data: impl Into<Arc<str>>) -> Self {
105 Self::Text(data.into())
106 }
107
108 pub fn custom<T>(value: T) -> Self
110 where
111 T: Any + Send + Sync,
112 {
113 Self::Any(Arc::new(value))
114 }
115
116 pub fn shared<T>(value: Arc<T>) -> Self
118 where
119 T: Any + Send + Sync,
120 {
121 Self::Any(value)
122 }
123
124 pub fn boxed<T>(value: Box<T>) -> Self
126 where
127 T: Any + Send + Sync,
128 {
129 let value: Arc<T> = Arc::from(value);
130 Self::Any(value)
131 }
132
133 pub fn kind(&self) -> &'static str {
135 match self {
136 Self::Audio(_) => "audio",
137 Self::F32(_) => "f32",
138 Self::F64(_) => "f64",
139 Self::Bytes(_) => "bytes",
140 Self::Text(_) => "text",
141 Self::Any(_) => "any",
142 }
143 }
144
145 pub fn as_f32(&self) -> Option<&[f32]> {
147 match self {
148 Self::F32(data) => Some(data.as_ref()),
149 _ => None,
150 }
151 }
152
153 pub fn as_audio(&self) -> Option<&AudioBuffer> {
155 match self {
156 Self::Audio(buffer) => Some(buffer),
157 _ => None,
158 }
159 }
160
161 pub fn as_f64(&self) -> Option<&[f64]> {
163 match self {
164 Self::F64(data) => Some(data.as_ref()),
165 _ => None,
166 }
167 }
168
169 pub fn as_bytes(&self) -> Option<&[u8]> {
171 match self {
172 Self::Bytes(data) => Some(data.as_ref()),
173 _ => None,
174 }
175 }
176
177 pub fn as_text(&self) -> Option<&str> {
179 match self {
180 Self::Text(data) => Some(data.as_ref()),
181 _ => None,
182 }
183 }
184
185 pub fn downcast<T>(&self) -> Option<Arc<T>>
187 where
188 T: Any + Send + Sync,
189 {
190 match self {
191 Self::Any(value) => value.clone().downcast::<T>().ok(),
192 _ => None,
193 }
194 }
195}
196
197#[derive(Clone, Debug)]
199pub struct AudioBuffer {
200 pub samples: Arc<[f32]>,
202 pub sample_rate: u32,
204 pub channels: u16,
206}
207
208impl AudioBuffer {
209 pub fn mono(samples: impl Into<Arc<[f32]>>, sample_rate: u32) -> Self {
211 Self {
212 samples: samples.into(),
213 sample_rate,
214 channels: 1,
215 }
216 }
217
218 pub fn frames(&self) -> usize {
220 self.samples.len() / self.channels as usize
221 }
222}
223
224#[derive(Debug, Clone, Default)]
226pub struct ResourceManager {
227 resources: HashMap<ResourceId, Resource>,
228}
229
230impl ResourceManager {
231 pub fn new() -> Self {
233 Self::default()
234 }
235
236 fn normalize_id(id: impl AsRef<str>) -> Result<ResourceId> {
237 ResourceId::new(id.as_ref())
238 }
239
240 pub fn len(&self) -> usize {
242 self.resources.len()
243 }
244
245 pub fn is_empty(&self) -> bool {
247 self.resources.is_empty()
248 }
249
250 pub fn get(&self, id: impl AsRef<str>) -> Option<&Resource> {
252 let id = Self::normalize_id(id).ok()?;
253 self.resources.get(&id)
254 }
255
256 pub fn get_cloned(&self, id: impl AsRef<str>) -> Option<Resource> {
258 self.get(id).cloned()
259 }
260
261 pub fn get_f32(&self, id: impl AsRef<str>) -> Option<&[f32]> {
263 self.get(id)?.as_f32()
264 }
265
266 pub fn get_f64(&self, id: impl AsRef<str>) -> Option<&[f64]> {
268 self.get(id)?.as_f64()
269 }
270
271 pub fn get_bytes(&self, id: impl AsRef<str>) -> Option<&[u8]> {
273 self.get(id)?.as_bytes()
274 }
275
276 pub fn get_text(&self, id: impl AsRef<str>) -> Option<&str> {
278 self.get(id)?.as_text()
279 }
280
281 pub fn get_custom<T>(&self, id: impl AsRef<str>) -> Option<Arc<T>>
283 where
284 T: Any + Send + Sync,
285 {
286 self.get(id)?.downcast::<T>()
287 }
288
289 pub fn require_f32(&self, id: impl AsRef<str>) -> Result<&[f32]> {
291 let id = Self::normalize_id(id)?;
292 let resource = self
293 .resources
294 .get(&id)
295 .ok_or_else(|| Error::ResourceNotFound(id.as_str().to_string()))?;
296
297 resource
298 .as_f32()
299 .ok_or_else(|| Error::ResourceTypeMismatch {
300 id: id.as_str().to_string(),
301 expected: "f32",
302 actual: resource.kind(),
303 })
304 }
305
306 pub fn contains(&self, id: impl AsRef<str>) -> bool {
308 self.get(id).is_some()
309 }
310
311 pub fn insert(&mut self, id: impl AsRef<str>, resource: Resource) -> Result<Option<Resource>> {
313 let id = Self::normalize_id(id)?;
314 Ok(self.resources.insert(id, resource))
315 }
316
317 pub fn add(&mut self, id: impl AsRef<str>, resource: Resource) -> Result<()> {
319 let id = Self::normalize_id(id)?;
320
321 if self.resources.contains_key(&id) {
322 return Err(Error::ResourceExists(id.as_str().to_string()));
323 }
324
325 self.resources.insert(id, resource);
326 Ok(())
327 }
328
329 pub fn replace(&mut self, id: impl AsRef<str>, resource: Resource) -> Result<Resource> {
331 let id = Self::normalize_id(id)?;
332
333 if !self.resources.contains_key(&id) {
334 return Err(Error::ResourceNotFound(id.as_str().to_string()));
335 }
336
337 Ok(self
338 .resources
339 .insert(id, resource)
340 .expect("resource existed before replace"))
341 }
342
343 pub fn remove(&mut self, id: impl AsRef<str>) -> Result<Resource> {
345 let id = Self::normalize_id(id)?;
346
347 self.resources
348 .remove(&id)
349 .ok_or_else(|| Error::ResourceNotFound(id.as_str().to_string()))
350 }
351
352 pub fn rename(&mut self, from: impl AsRef<str>, to: impl AsRef<str>) -> Result<()> {
354 let from = Self::normalize_id(from)?;
355 let to = Self::normalize_id(to)?;
356
357 if from == to {
358 return Ok(());
359 }
360
361 if self.resources.contains_key(&to) {
362 return Err(Error::ResourceExists(to.as_str().to_string()));
363 }
364
365 let resource = self
366 .resources
367 .remove(&from)
368 .ok_or_else(|| Error::ResourceNotFound(from.as_str().to_string()))?;
369
370 self.resources.insert(to, resource);
371 Ok(())
372 }
373
374 pub fn clear(&mut self) {
376 self.resources.clear();
377 }
378
379 pub fn iter(&self) -> impl Iterator<Item = (&ResourceId, &Resource)> {
381 self.resources.iter()
382 }
383
384 pub fn snapshot(&self) -> Vec<(ResourceId, Resource)> {
386 self.resources
387 .iter()
388 .map(|(id, resource)| (id.clone(), resource.clone()))
389 .collect()
390 }
391
392 pub fn prune_except<I, S>(&mut self, keep: I) -> Vec<(ResourceId, Resource)>
394 where
395 I: IntoIterator<Item = S>,
396 S: AsRef<str>,
397 {
398 let keep: HashSet<String> = keep.into_iter().map(|id| id.as_ref().to_string()).collect();
399
400 let mut removed = Vec::new();
401 self.resources.retain(|id, resource| {
402 if keep.contains(id.as_str()) {
403 true
404 } else {
405 removed.push((id.clone(), resource.clone()));
406 false
407 }
408 });
409
410 removed
411 }
412}