use std::{
collections::{BinaryHeap, HashMap, BTreeSet},
rc::Rc,
};
use super::{loader::Loader, loader_gltf::LoaderGLTF, loader_off::LoaderOff, ExtensionMap};
#[derive(Clone)]
struct LoaderEntry {
pub loader: Rc<dyn Loader>,
pub priority: u32,
}
impl LoaderEntry {
pub fn new(loader: Rc<dyn Loader>) -> Self {
let priority = loader.get_priority();
Self { loader, priority }
}
}
impl Ord for LoaderEntry {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.priority.cmp(&other.priority)
}
}
impl PartialOrd for LoaderEntry {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.priority.partial_cmp(&other.priority)
}
}
impl PartialEq for LoaderEntry {
fn eq(&self, other: &Self) -> bool {
self.priority == other.priority
}
}
impl Eq for LoaderEntry {}
type LoaderList = BinaryHeap<LoaderEntry>;
type LoaderMap = HashMap<String, LoaderList>;
pub struct Manager {
loader: Vec<Rc<dyn Loader>>,
map_ext: ExtensionMap,
map_mime: LoaderMap,
}
impl Manager {
pub fn new() -> Self {
let mut result = Self::new_empty();
result.register_loader(Box::new(LoaderOff::new()));
result.register_loader(Box::new(LoaderGLTF::new()));
result
}
pub fn new_empty() -> Self {
Self {
loader: Vec::new(),
map_ext: ExtensionMap::new(),
map_mime: HashMap::new(),
}
}
pub fn register_loader(&mut self, loader: Box<dyn Loader>) {
let mut ext_map = loader.as_ref().get_extensions_mime_type_map();
let mime_types = loader.get_mime_types();
let loader: Rc<dyn Loader> = loader.into();
let loader_entry = LoaderEntry::new(loader.clone());
self.loader.push(loader);
for (ext, new_mime_types) in ext_map.iter_mut() {
let mime_types = self
.map_ext
.entry(ext.clone())
.or_insert_with(|| BTreeSet::new());
mime_types.append(new_mime_types);
}
for mim_type in mime_types.iter() {
let loader_list = self
.map_mime
.entry(mim_type.clone())
.or_insert_with(|| LoaderList::new());
loader_list.push(loader_entry.clone());
}
}
pub fn get_mime_types_for_extension(&self, ext: &str) -> Vec<String> {
let ext = ext.to_lowercase();
match self.map_ext.get(&ext) {
Some(lst) => {
Vec::from_iter(lst.iter().map(|s| s.clone()))
}
None => Vec::new(),
}
}
pub fn get_loader_by_mime_type(&self, mime_type: &str) -> Option<Rc<dyn Loader>> {
let mime_type = mime_type.to_lowercase();
match self.map_mime.get(&mime_type) {
Some(lst) => {
let e = match lst.peek() {
Some(l) => Some(l.loader.clone()),
None => None,
};
e
}
None => None,
}
}
pub fn get_loader_list(&self) -> &[Rc<dyn Loader>] {
&self.loader
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use crate::{loader::Resource, structure::CADData, Error};
use super::*;
struct FakeLoader {
identifier: String,
map_ext: ExtensionMap,
mime_types: Vec<String>,
priority: u32,
}
impl FakeLoader {
pub fn new(
identifier: String,
map_ext: ExtensionMap,
mime_types: Vec<String>,
priority: u32,
) -> Self {
Self {
identifier,
map_ext,
mime_types,
priority,
}
}
}
impl Loader for FakeLoader {
fn get_mime_types(&self) -> Vec<String> {
self.mime_types.clone()
}
fn get_extensions_mime_type_map(&self) -> ExtensionMap {
self.map_ext.clone()
}
fn get_priority(&self) -> u32 {
self.priority
}
fn read(&self, _resource: &dyn Resource) -> Result<CADData, Error> {
todo!()
}
fn get_name(&self) -> &str {
&self.identifier
}
}
#[test]
fn test_loader_registry() {
let mut m = Manager::new_empty();
let l = FakeLoader::new(
"loader1".to_owned(),
BTreeMap::from([("foobar".to_owned(), BTreeSet::from(["foobar/x-test".to_owned()]))]),
vec!["foobar/x-test".to_owned()],
42,
);
m.register_loader(Box::new(l));
assert_eq!(m.get_mime_types_for_extension("foobar"), ["foobar/x-test"]);
assert_eq!(m.get_mime_types_for_extension("FOobar"), ["foobar/x-test"]);
assert!(m.get_mime_types_for_extension("FOobar2").is_empty());
assert!(m.get_mime_types_for_extension("FOob").is_empty());
let l2 = FakeLoader::new(
"loader2".to_owned(),
BTreeMap::from([("foobar".to_owned(), BTreeSet::from(["foobar/x-test".to_owned()]))]),
vec!["foobar/x-test".to_owned()],
43,
);
m.register_loader(Box::new(l2));
assert_eq!(m.get_mime_types_for_extension("foobar"), ["foobar/x-test"]);
assert_eq!(m.get_mime_types_for_extension("FOobar"), ["foobar/x-test"]);
assert!(m.get_mime_types_for_extension("FOobar2").is_empty());
assert!(m.get_mime_types_for_extension("FOob").is_empty());
assert_eq!(
m.get_loader_by_mime_type("foobar/x-test").unwrap().get_priority(),
43
);
assert_eq!(
m.get_loader_by_mime_type("foobar/x-test").unwrap().get_name(),
"loader2"
);
assert_eq!(m.get_loader_list().len(), 2);
}
#[test]
fn test_if_loaders_are_registered() {
let manager = Manager::new();
let off_mime_types = manager.get_mime_types_for_extension("off");
assert_eq!(off_mime_types.len(), 1);
let loader = manager.get_loader_by_mime_type(&off_mime_types[0]).unwrap();
assert_eq!(loader.get_name(), "Object File Format");
let loader = manager.get_loader_by_mime_type("model/vnd.off").unwrap();
assert_eq!(loader.get_name(), "Object File Format");
let loaders = manager.get_loader_list();
assert_eq!(loaders.len(), 2);
}
#[test]
fn test_extension_map() {
let manager = Manager::new();
for loader in manager.get_loader_list() {
let mut mime_types_set: BTreeSet<String> = BTreeSet::new();
let mut ext_map = loader.get_extensions_mime_type_map();
for m in ext_map.values_mut() {
mime_types_set.append(m);
}
let mut mime_types: Vec<String> = loader.get_mime_types();
mime_types.sort();
assert_eq!(mime_types.len(), mime_types_set.len());
for m in mime_types.iter() {
assert!(mime_types_set.contains(m));
}
}
}
}