use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ResourceType {
Font,
Image,
VectorGraphic,
ColorSpace,
DrawParam,
Other,
}
#[derive(Debug, Clone)]
pub struct ResourceItem {
pub id: String,
pub resource_type: ResourceType,
pub file_path: Option<String>,
pub format: Option<String>,
}
impl ResourceItem {
#[must_use]
pub fn new(id: impl Into<String>, resource_type: ResourceType) -> Self {
Self {
id: id.into(),
resource_type,
file_path: None,
format: None,
}
}
#[must_use]
pub fn with_file_path(mut self, path: impl Into<String>) -> Self {
self.file_path = Some(path.into());
self
}
#[must_use]
pub fn with_format(mut self, format: impl Into<String>) -> Self {
self.format = Some(format.into());
self
}
}
#[derive(Debug, Clone, Default)]
pub struct ResourceManage {
resources: HashMap<String, ResourceItem>,
by_type: HashMap<ResourceType, Vec<String>>,
}
impl ResourceManage {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn from_items(items: Vec<ResourceItem>) -> Self {
let mut mgr = Self::new();
for item in items {
mgr.insert(item);
}
mgr
}
pub fn insert(&mut self, item: ResourceItem) {
let id = item.id.clone();
let resource_type = item.resource_type;
self.by_type
.entry(resource_type)
.or_default()
.push(id.clone());
self.resources.insert(id, item);
}
#[must_use]
pub fn get(&self, id: &str) -> Option<&ResourceItem> {
self.resources.get(id)
}
#[must_use]
pub fn all(&self) -> &HashMap<String, ResourceItem> {
&self.resources
}
#[must_use]
pub fn ids_by_type(&self, resource_type: ResourceType) -> &[String] {
self.by_type.get(&resource_type).map_or(&[], Vec::as_slice)
}
#[must_use]
pub fn items_by_type(&self, resource_type: ResourceType) -> Vec<&ResourceItem> {
self.ids_by_type(resource_type)
.iter()
.filter_map(|id| self.resources.get(id))
.collect()
}
#[must_use]
pub fn len(&self) -> usize {
self.resources.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.resources.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_manager() {
let mgr = ResourceManage::new();
assert!(mgr.is_empty());
assert_eq!(mgr.len(), 0);
assert!(mgr.get("nonexistent").is_none());
}
#[test]
fn test_insert_and_get() {
let mut mgr = ResourceManage::new();
mgr.insert(
ResourceItem::new("1", ResourceType::Image)
.with_file_path("Res/image.png")
.with_format("PNG"),
);
assert_eq!(mgr.len(), 1);
let item = mgr.get("1").unwrap();
assert_eq!(item.resource_type, ResourceType::Image);
assert_eq!(item.file_path.as_deref(), Some("Res/image.png"));
}
#[test]
fn test_from_items() {
let items = vec![
ResourceItem::new("1", ResourceType::Font),
ResourceItem::new("2", ResourceType::Image),
ResourceItem::new("3", ResourceType::Font),
];
let mgr = ResourceManage::from_items(items);
assert_eq!(mgr.len(), 3);
assert_eq!(mgr.ids_by_type(ResourceType::Font).len(), 2);
assert_eq!(mgr.ids_by_type(ResourceType::Image).len(), 1);
}
#[test]
fn test_items_by_type() {
let mut mgr = ResourceManage::new();
mgr.insert(ResourceItem::new("10", ResourceType::ColorSpace));
mgr.insert(ResourceItem::new("20", ResourceType::DrawParam));
mgr.insert(ResourceItem::new("30", ResourceType::ColorSpace));
let cs_items = mgr.items_by_type(ResourceType::ColorSpace);
assert_eq!(cs_items.len(), 2);
}
#[test]
fn test_insert_overwrite() {
let mut mgr = ResourceManage::new();
mgr.insert(ResourceItem::new("1", ResourceType::Image));
mgr.insert(ResourceItem::new("1", ResourceType::Font));
assert_eq!(mgr.len(), 1);
assert_eq!(mgr.get("1").unwrap().resource_type, ResourceType::Font);
}
}