use std::sync::{Arc, RwLock, RwLockReadGuard};
pub struct FontManager<Font> {
font_data: Arc<RwLock<Box<dyn FontsHolder<Font>>>>,
}
pub type FontsGuard<'a, Font> = RwLockReadGuard<'a, Box<dyn FontsHolder<Font>>>;
impl<Font> FontManager<Font> {
pub fn new<Holder: FontsHolder<Font> + 'static>(font_data: Holder) -> Self {
Self {
font_data: Arc::new(RwLock::new(Box::new(font_data))),
}
}
pub fn leak(self) -> &'static Self {
Box::leak(Box::new(self))
}
pub fn get_fonts(&self) -> FontsGuard<'_, Font> {
self.font_data.read().unwrap()
}
pub fn push_font(&self, font: Font) -> usize {
self.font_data.write().unwrap().push_font(font)
}
pub fn delete_font(&self, index: usize) {
self.font_data.write().unwrap().delete_font(index);
}
}
pub trait FontsHolder<Font> {
fn get_fonts(&self) -> &[Font];
fn push_font(&mut self, font: Font) -> usize;
fn delete_font(&mut self, index: usize);
}
pub struct GenericFontsHolder<Font> {
fonts: Vec<Font>,
free_fonts: Vec<usize>,
}
impl<Font> Default for GenericFontsHolder<Font> {
fn default() -> Self {
Self::new()
}
}
impl<Font> GenericFontsHolder<Font> {
pub fn new() -> Self {
Self {
fonts: vec![],
free_fonts: vec![],
}
}
}
impl<Font> FontsHolder<Font> for GenericFontsHolder<Font> {
fn get_fonts(&self) -> &[Font] {
self.fonts.as_slice()
}
fn push_font(&mut self, font: Font) -> usize {
if let Some(index) = self.free_fonts.pop() {
self.fonts.insert(index, font);
index
} else {
self.fonts.push(font);
self.fonts.len() - 1
}
}
fn delete_font(&mut self, index: usize) {
self.free_fonts.push(index);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generic_fonts_holder_reuses_deleted_slots() {
let mut holder = GenericFontsHolder::new();
assert_eq!(holder.push_font("a"), 0);
assert_eq!(holder.push_font("b"), 1);
holder.delete_font(0);
assert_eq!(holder.push_font("c"), 0);
assert_eq!(holder.get_fonts()[0], "c");
assert_eq!(holder.get_fonts()[1], "a");
assert_eq!(holder.get_fonts()[2], "b");
}
#[test]
fn font_manager_round_trips_through_rwlock() {
let manager = FontManager::new(GenericFontsHolder::new());
assert_eq!(manager.push_font("font-a"), 0);
assert_eq!(manager.get_fonts().get_fonts()[0], "font-a");
manager.delete_font(0);
assert_eq!(manager.push_font("font-b"), 0);
assert_eq!(manager.get_fonts().get_fonts()[0], "font-b");
}
}