1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::Path;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum ResourceType {
10 Model,
12 Source,
14 Data,
16 Config,
18 Documentation,
20 License,
22 Binary,
24 Text,
26 Metadata,
28}
29
30impl ResourceType {
31 pub fn extension(&self) -> &'static str {
33 match self {
34 ResourceType::Model => "model",
35 ResourceType::Source => "rs",
36 ResourceType::Data => "data",
37 ResourceType::Config => "toml",
38 ResourceType::Documentation => "md",
39 ResourceType::License => "txt",
40 ResourceType::Binary => "bin",
41 ResourceType::Text => "txt",
42 ResourceType::Metadata => "json",
43 }
44 }
45
46 pub fn mime_type(&self) -> &'static str {
48 match self {
49 ResourceType::Model => "application/octet-stream",
50 ResourceType::Source => "text/x-rust",
51 ResourceType::Data => "application/octet-stream",
52 ResourceType::Config => "text/x-toml",
53 ResourceType::Documentation => "text/markdown",
54 ResourceType::License => "text/plain",
55 ResourceType::Binary => "application/octet-stream",
56 ResourceType::Text => "text/plain",
57 ResourceType::Metadata => "application/json",
58 }
59 }
60
61 pub fn from_extension(ext: &str) -> Self {
63 match ext.to_lowercase().as_str() {
64 "model" | "pth" | "pt" | "torsh" => ResourceType::Model,
65 "rs" => ResourceType::Source,
66 "json" => ResourceType::Metadata,
67 "toml" | "yaml" | "yml" => ResourceType::Config,
68 "md" | "rst" => ResourceType::Documentation,
69 "txt" | "license" => ResourceType::License,
70 "bin" | "dat" => ResourceType::Binary,
71 _ => ResourceType::Data,
72 }
73 }
74}
75
76#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
78pub struct Resource {
79 pub name: String,
81
82 pub resource_type: ResourceType,
84
85 pub data: Vec<u8>,
87
88 pub metadata: HashMap<String, String>,
90}
91
92impl Resource {
93 pub fn new(name: String, resource_type: ResourceType, data: Vec<u8>) -> Self {
95 Self {
96 name,
97 resource_type,
98 data,
99 metadata: HashMap::new(),
100 }
101 }
102
103 pub fn from_file<P: AsRef<Path>>(
105 path: P,
106 resource_type: Option<ResourceType>,
107 ) -> std::io::Result<Self> {
108 let path = path.as_ref();
109 let name = path
110 .file_name()
111 .and_then(|n| n.to_str())
112 .unwrap_or("unnamed")
113 .to_string();
114
115 let resource_type = resource_type.unwrap_or_else(|| {
116 path.extension()
117 .and_then(|e| e.to_str())
118 .map(ResourceType::from_extension)
119 .unwrap_or(ResourceType::Data)
120 });
121
122 let data = std::fs::read(path)?;
123
124 Ok(Self::new(name, resource_type, data))
125 }
126
127 pub fn size(&self) -> usize {
129 self.data.len()
130 }
131
132 pub fn sha256(&self) -> String {
134 use sha2::{Digest, Sha256};
135 let mut hasher = Sha256::new();
136 hasher.update(&self.data);
137 hex::encode(hasher.finalize())
138 }
139
140 pub fn add_metadata(&mut self, key: String, value: String) {
142 self.metadata.insert(key, value);
143 }
144
145 pub fn get_metadata(&self, key: &str) -> Option<&str> {
147 self.metadata.get(key).map(|s| s.as_str())
148 }
149
150 pub fn is_compressed(&self) -> bool {
152 self.metadata.contains_key("compression")
153 }
154
155 pub fn compression_method(&self) -> Option<&str> {
157 self.get_metadata("compression")
158 }
159}
160
161pub struct ResourceCollection {
163 resources: HashMap<String, Resource>,
164}
165
166impl ResourceCollection {
167 pub fn new() -> Self {
169 Self {
170 resources: HashMap::new(),
171 }
172 }
173
174 pub fn add(&mut self, resource: Resource) -> Result<(), String> {
176 if self.resources.contains_key(&resource.name) {
177 return Err(format!("Resource '{}' already exists", resource.name));
178 }
179
180 self.resources.insert(resource.name.clone(), resource);
181 Ok(())
182 }
183
184 pub fn get(&self, name: &str) -> Option<&Resource> {
186 self.resources.get(name)
187 }
188
189 pub fn get_mut(&mut self, name: &str) -> Option<&mut Resource> {
191 self.resources.get_mut(name)
192 }
193
194 pub fn remove(&mut self, name: &str) -> Option<Resource> {
196 self.resources.remove(name)
197 }
198
199 pub fn list(&self) -> Vec<&str> {
201 self.resources.keys().map(|s| s.as_str()).collect()
202 }
203
204 pub fn by_type(&self, resource_type: ResourceType) -> Vec<&Resource> {
206 self.resources
207 .values()
208 .filter(|r| r.resource_type == resource_type)
209 .collect()
210 }
211
212 pub fn total_size(&self) -> usize {
214 self.resources.values().map(|r| r.size()).sum()
215 }
216
217 pub fn clear(&mut self) {
219 self.resources.clear();
220 }
221
222 pub fn len(&self) -> usize {
224 self.resources.len()
225 }
226
227 pub fn is_empty(&self) -> bool {
229 self.resources.is_empty()
230 }
231}
232
233impl Default for ResourceCollection {
234 fn default() -> Self {
235 Self::new()
236 }
237}
238
239pub struct ResourceFilter {
241 types: Option<Vec<ResourceType>>,
242 name_pattern: Option<String>,
243 min_size: Option<usize>,
244 max_size: Option<usize>,
245}
246
247impl ResourceFilter {
248 pub fn new() -> Self {
250 Self {
251 types: None,
252 name_pattern: None,
253 min_size: None,
254 max_size: None,
255 }
256 }
257
258 pub fn with_types(mut self, types: Vec<ResourceType>) -> Self {
260 self.types = Some(types);
261 self
262 }
263
264 pub fn with_name_pattern(mut self, pattern: String) -> Self {
266 self.name_pattern = Some(pattern);
267 self
268 }
269
270 pub fn with_size_range(mut self, min: Option<usize>, max: Option<usize>) -> Self {
272 self.min_size = min;
273 self.max_size = max;
274 self
275 }
276
277 pub fn matches(&self, resource: &Resource) -> bool {
279 if let Some(types) = &self.types {
281 if !types.contains(&resource.resource_type) {
282 return false;
283 }
284 }
285
286 if let Some(pattern) = &self.name_pattern {
288 if !resource.name.contains(pattern) {
289 return false;
290 }
291 }
292
293 let size = resource.size();
295 if let Some(min) = self.min_size {
296 if size < min {
297 return false;
298 }
299 }
300 if let Some(max) = self.max_size {
301 if size > max {
302 return false;
303 }
304 }
305
306 true
307 }
308}
309
310impl Default for ResourceFilter {
311 fn default() -> Self {
312 Self::new()
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_resource_type() {
322 assert_eq!(ResourceType::from_extension("rs"), ResourceType::Source);
323 assert_eq!(ResourceType::from_extension("model"), ResourceType::Model);
324 assert_eq!(ResourceType::from_extension("json"), ResourceType::Metadata);
325 assert_eq!(ResourceType::from_extension("unknown"), ResourceType::Data);
326 }
327
328 #[test]
329 fn test_resource_creation() {
330 let resource = Resource::new(
331 "test.model".to_string(),
332 ResourceType::Model,
333 vec![1, 2, 3, 4],
334 );
335
336 assert_eq!(resource.name, "test.model");
337 assert_eq!(resource.resource_type, ResourceType::Model);
338 assert_eq!(resource.size(), 4);
339 }
340
341 #[test]
342 fn test_resource_collection() {
343 let mut collection = ResourceCollection::new();
344
345 let resource1 = Resource::new("res1".to_string(), ResourceType::Model, vec![1, 2, 3]);
346 let resource2 = Resource::new("res2".to_string(), ResourceType::Data, vec![4, 5, 6, 7]);
347
348 collection.add(resource1).unwrap();
349 collection.add(resource2).unwrap();
350
351 assert_eq!(collection.len(), 2);
352 assert_eq!(collection.total_size(), 7);
353
354 let models = collection.by_type(ResourceType::Model);
355 assert_eq!(models.len(), 1);
356 }
357
358 #[test]
359 fn test_resource_filter() {
360 let resource = Resource::new("test.model".to_string(), ResourceType::Model, vec![0; 100]);
361
362 let filter = ResourceFilter::new()
363 .with_types(vec![ResourceType::Model, ResourceType::Data])
364 .with_size_range(Some(50), Some(200));
365
366 assert!(filter.matches(&resource));
367
368 let filter2 = ResourceFilter::new()
369 .with_types(vec![ResourceType::Source])
370 .with_size_range(Some(50), Some(200));
371
372 assert!(!filter2.matches(&resource));
373 }
374}