diff options
Diffstat (limited to 'src/loading')
| -rw-r--r-- | src/loading/fs.rs | 112 | ||||
| -rw-r--r-- | src/loading/mod.rs | 47 |
2 files changed, 63 insertions, 96 deletions
diff --git a/src/loading/fs.rs b/src/loading/fs.rs index c3ca332e..9289519c 100644 --- a/src/loading/fs.rs +++ b/src/loading/fs.rs @@ -1,8 +1,6 @@ -use std::cell::{Ref, RefCell}; -use std::collections::HashMap; use std::fs::{self, File}; use std::io; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::rc::Rc; use memmap2::Mmap; @@ -10,9 +8,8 @@ use same_file::Handle; use ttf_parser::{name_id, Face}; use walkdir::WalkDir; -use super::{FileId, Loader}; +use super::{FileHash, Loader}; use crate::font::{FaceInfo, FontStretch, FontStyle, FontVariant, FontWeight}; -use crate::util::PathExt; /// Loads fonts and images from the local file system. /// @@ -20,13 +17,12 @@ use crate::util::PathExt; #[derive(Debug, Default, Clone)] pub struct FsLoader { faces: Vec<FaceInfo>, - paths: RefCell<HashMap<FileId, PathBuf>>, } impl FsLoader { /// Create a new loader without any fonts. pub fn new() -> Self { - Self { faces: vec![], paths: RefCell::default() } + Self { faces: vec![] } } /// Builder-style variant of `search_system`. @@ -52,51 +48,6 @@ impl FsLoader { self.search_system_impl(); } - /// Search for all fonts at a path. - /// - /// If the path is a directory, all contained fonts will be searched for - /// recursively. - pub fn search_path(&mut self, dir: impl AsRef<Path>) { - let walk = WalkDir::new(dir) - .follow_links(true) - .sort_by(|a, b| a.file_name().cmp(b.file_name())) - .into_iter() - .filter_map(|e| e.ok()); - - for entry in walk { - let path = entry.path(); - if let Some(ext) = path.extension().and_then(|s| s.to_str()) { - match ext { - #[rustfmt::skip] - "ttf" | "otf" | "TTF" | "OTF" | - "ttc" | "otc" | "TTC" | "OTC" => { - self.search_file(path).ok(); - } - _ => {} - } - } - } - } - - /// Resolve a file id for a path. - pub fn resolve(&self, path: &Path) -> io::Result<FileId> { - let file = File::open(path)?; - let meta = file.metadata()?; - if meta.is_file() { - let handle = Handle::from_file(file)?; - let id = FileId(fxhash::hash64(&handle)); - self.paths.borrow_mut().insert(id, path.normalize()); - Ok(id) - } else { - Err(io::Error::new(io::ErrorKind::Other, "not a file")) - } - } - - /// Return the path of a resolved file. - pub fn path(&self, id: FileId) -> Ref<Path> { - Ref::map(self.paths.borrow(), |paths| paths[&id].as_path()) - } - #[cfg(all(unix, not(target_os = "macos")))] fn search_system_impl(&mut self) { self.search_path("/usr/share/fonts"); @@ -134,6 +85,32 @@ impl FsLoader { } } + /// Search for all fonts at a path. + /// + /// If the path is a directory, all contained fonts will be searched for + /// recursively. + pub fn search_path(&mut self, dir: impl AsRef<Path>) { + let walk = WalkDir::new(dir) + .follow_links(true) + .sort_by(|a, b| a.file_name().cmp(b.file_name())) + .into_iter() + .filter_map(|e| e.ok()); + + for entry in walk { + let path = entry.path(); + if let Some(ext) = path.extension().and_then(|s| s.to_str()) { + match ext { + #[rustfmt::skip] + "ttf" | "otf" | "TTF" | "OTF" | + "ttc" | "otc" | "TTC" | "OTC" => { + self.search_file(path).ok(); + } + _ => {} + } + } + } + } + /// Index the font faces in the file at the given path. /// /// The file may form a font collection and contain multiple font faces, @@ -180,8 +157,12 @@ impl FsLoader { stretch: FontStretch::from_number(face.width().to_number()), }; - let file = self.resolve(path)?; - self.faces.push(FaceInfo { file, index, family, variant }); + self.faces.push(FaceInfo { + path: path.to_owned(), + index, + family, + variant, + }); Ok(()) } @@ -192,16 +173,19 @@ impl Loader for FsLoader { &self.faces } - fn resolve_from(&self, base: FileId, path: &Path) -> io::Result<FileId> { - let full = self.paths.borrow()[&base] - .parent() - .expect("base is a file") - .join(path); - self.resolve(&full) + fn resolve(&self, path: &Path) -> io::Result<FileHash> { + let file = File::open(path)?; + let meta = file.metadata()?; + if meta.is_file() { + let handle = Handle::from_file(file)?; + Ok(FileHash(fxhash::hash64(&handle))) + } else { + Err(io::Error::new(io::ErrorKind::Other, "not a file")) + } } - fn load_file(&self, id: FileId) -> io::Result<Vec<u8>> { - fs::read(&self.paths.borrow()[&id]) + fn load(&self, path: &Path) -> io::Result<Vec<u8>> { + fs::read(path) } } @@ -211,8 +195,8 @@ mod tests { #[test] fn test_index_font_dir() { - let map = FsLoader::new().with_path("fonts").paths.into_inner(); - let mut paths: Vec<_> = map.into_iter().map(|p| p.1).collect(); + let faces = FsLoader::new().with_path("fonts").faces; + let mut paths: Vec<_> = faces.into_iter().map(|info| info.path).collect(); paths.sort(); assert_eq!(paths, [ diff --git a/src/loading/mod.rs b/src/loading/mod.rs index 65eb25c6..7d697310 100644 --- a/src/loading/mod.rs +++ b/src/loading/mod.rs @@ -13,41 +13,24 @@ use serde::{Deserialize, Serialize}; use crate::font::FaceInfo; +/// A hash that identifies a file. +/// +/// Such a hash can be [resolved](Loader::resolve) from a path. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Serialize, Deserialize)] +pub struct FileHash(pub u64); + /// Loads resources from a local or remote source. pub trait Loader { /// Descriptions of all font faces this loader serves. fn faces(&self) -> &[FaceInfo]; - /// Resolve a `path` relative to a `base` file. - /// - /// This should return the same id for all paths pointing to the same file - /// and `None` if the file does not exist. - fn resolve_from(&self, base: FileId, path: &Path) -> io::Result<FileId>; - - /// Load a file by id. - /// - /// This must only be called with an `id` returned by a call to this - /// loader's `resolve_from` method. - fn load_file(&self, id: FileId) -> io::Result<Vec<u8>>; -} - -/// A file id that can be [resolved](Loader::resolve_from) from a path. -/// -/// Should be the same for all paths pointing to the same file. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[derive(Serialize, Deserialize)] -pub struct FileId(u64); + /// Resolve a hash that is the same for this and all other paths pointing to + /// the same file. + fn resolve(&self, path: &Path) -> io::Result<FileHash>; -impl FileId { - /// Create a file id from a raw value. - pub const fn from_raw(v: u64) -> Self { - Self(v) - } - - /// Convert into the raw underlying value. - pub const fn into_raw(self) -> u64 { - self.0 - } + /// Load a file from a path. + fn load(&self, path: &Path) -> io::Result<Vec<u8>>; } /// A loader which serves nothing. @@ -58,11 +41,11 @@ impl Loader for BlankLoader { &[] } - fn resolve_from(&self, _: FileId, _: &Path) -> io::Result<FileId> { + fn resolve(&self, _: &Path) -> io::Result<FileHash> { Err(io::ErrorKind::NotFound.into()) } - fn load_file(&self, _: FileId) -> io::Result<Vec<u8>> { - panic!("resolve_from never returns an id") + fn load(&self, _: &Path) -> io::Result<Vec<u8>> { + Err(io::ErrorKind::NotFound.into()) } } |
