use alloc::rc::Rc;
use core::cell::RefCell;
use core::fmt::{self, Debug, Formatter};
use parley::FontContext;
use waterui_core::Environment;
const NOT_INSTALLED: &str = "no font collection is installed in the environment: \
the host must install exactly one at startup with `FontCollection::install`, \
before it hands the environment to the application";
#[derive(Clone)]
pub struct FontCollection(Rc<RefCell<FontContext>>);
impl Debug for FontCollection {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("FontCollection")
.field("identity", &self.identity())
.finish()
}
}
impl FontCollection {
#[must_use]
pub fn new(fonts: FontContext) -> Self {
Self(Rc::new(RefCell::new(fonts)))
}
#[cfg(feature = "system-fonts")]
#[must_use]
pub fn system() -> Self {
Self::new(FontContext::new())
}
pub fn install(self, env: &mut Environment) {
env.insert(self);
}
#[must_use]
pub fn from_env(env: &Environment) -> Self {
env.get::<Self>().cloned().expect(NOT_INSTALLED)
}
pub fn use_fonts<R>(&self, use_fonts: impl FnOnce(&mut FontContext) -> R) -> R {
use_fonts(&mut self.0.borrow_mut())
}
#[must_use]
pub fn identity(&self) -> usize {
Rc::as_ptr(&self.0) as usize
}
}
#[cfg(test)]
mod tests {
use waterui_core::Environment;
use super::{FontCollection, NOT_INSTALLED};
fn empty() -> FontCollection {
FontCollection::new(parley::FontContext {
collection: parley::fontique::Collection::new(parley::fontique::CollectionOptions {
system_fonts: false,
..parley::fontique::CollectionOptions::default()
}),
source_cache: parley::fontique::SourceCache::default(),
})
}
#[test]
fn two_components_share_one_collection() {
let mut env = Environment::new();
empty().install(&mut env);
let first = FontCollection::from_env(&env);
let second = FontCollection::from_env(&env);
assert_eq!(
first.identity(),
second.identity(),
"every component must read back the one collection the host installed"
);
}
#[test]
fn an_extended_environment_keeps_the_collection() {
let mut env = Environment::new();
let installed = empty();
let identity = installed.identity();
installed.install(&mut env);
let child = env.extending(7_u32);
assert_eq!(FontCollection::from_env(&child).identity(), identity);
}
#[test]
fn separate_collections_are_told_apart() {
assert_ne!(empty().identity(), empty().identity());
}
#[test]
#[should_panic(expected = "no font collection is installed in the environment")]
fn reading_an_uninstalled_collection_names_the_host() {
let _ = FontCollection::from_env(&Environment::new());
}
#[test]
fn the_loan_reaches_the_installed_collection() {
let collection = empty();
let first = collection.use_fonts(|fonts| fonts.collection.family_names().count());
let second = collection.use_fonts(|fonts| fonts.collection.family_names().count());
assert_eq!(first, second);
}
#[test]
fn the_panic_names_the_hosts_responsibility() {
assert!(NOT_INSTALLED.contains("the host must install"));
}
#[test]
fn the_installer_gives_a_bare_host_a_collection() {
let mut env = Environment::new();
crate::install_system_font_collection(&mut env);
let installed = FontCollection::from_env(&env).identity();
assert_eq!(FontCollection::from_env(&env).identity(), installed);
}
#[test]
fn the_installer_yields_to_a_collection_the_host_already_owns() {
let mut env = Environment::new();
let owned = empty();
let identity = owned.identity();
owned.install(&mut env);
crate::install_system_font_collection(&mut env);
assert_eq!(FontCollection::from_env(&env).identity(), identity);
}
}