//! Module handling the resources used for captioning. mod filesystem; mod fonts; mod templates; pub use self::filesystem::{BytesLoader, FileLoader}; pub use self::fonts::{Font, FontLoader, FontError, FILE_EXTENSION as FONT_FILE_EXTENSION}; pub use self::templates::{DEFAULT_IMAGE_FORMAT, IMAGE_FORMAT_EXTENSIONS, Template, TemplateLoader, TemplateError}; use std::error::Error; use std::fmt; use std::sync::Arc; use util::cache::ThreadSafeCache; /// Loader of resources from some external source. pub trait Loader { /// Type of resources that this loader can load. type Item; /// Error that may occur while loading the resource. type Err: Error; /// Load a resource of given name. fn load<'n>(&self, name: &'n str) -> Result; } /// Type of a loader that doles out shared references to the resources. pub type SharingLoader = Loader, Err=E>; /// A loader that keeps a cache of resources previously loaded. pub struct CachingLoader { inner: L, cache: ThreadSafeCache, pub(crate) phony: bool, } impl CachingLoader { /// Wrap a `Loader` with a `CachingLoader` of given capacity. #[inline] pub fn new(inner: L, capacity: usize) -> Self { CachingLoader{ inner: inner, cache: ThreadSafeCache::new(capacity), phony: false, } } /// Create a phony version of CachingLoader that doesn't actually cache anything. /// /// This is used to transparently wrap a Loader into Loader>, /// which is necessary because Rust cannot really abstract between the two. #[inline] pub(crate) fn phony(inner: L) -> Self { CachingLoader{ inner: inner, cache: ThreadSafeCache::new(1), phony: true, } } } impl CachingLoader { /// Returns a reference to the loader's cache. #[inline] pub fn cache(&self) -> &ThreadSafeCache { &self.cache } } impl Loader for CachingLoader { type Item = Arc; type Err = L::Err; /// Load the object from cache or fall back on the original Loader. /// Cache the objects loaded this way. fn load<'n>(&self, name: &'n str) -> Result { if self.phony { let obj = self.inner.load(name)?; Ok(Arc::new(obj)) } else { if let Some(obj) = self.cache.get(name) { return Ok(obj); } let obj = self.inner.load(name)?; let cached_obj = self.cache.put(name.to_owned(), obj); Ok(cached_obj) } } } // TODO: add impl when specialization is stable impl fmt::Debug for CachingLoader { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("CachingLoader") .field("inner", &"...") .field("cache", &self.cache) .field("phony", &self.phony) .finish() } }