Skip to content

Commit cc1e973

Browse files
committed
Address review comments
1 parent 19320a3 commit cc1e973

6 files changed

Lines changed: 74 additions & 65 deletions

File tree

src/datastore.rs

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
};
99

1010
use crate::error::{StoreEntryError, StoreError};
11-
use crate::font_source::FontSource;
11+
use crate::font_source::{DirEntry, FontSource};
1212

1313
/// A generic file store for UFO [data][spec_data] and [images][spec_images],
1414
/// mapping [`PathBuf`] keys to [`Vec<u8>`] values.
@@ -141,13 +141,14 @@ impl DataType for Data {
141141
.list_dir(&dir_path)
142142
.map_err(|e| StoreEntryError::new(dir_path.clone(), e.into()))?;
143143

144-
for (entry_name, is_dir) in entries {
145-
let full_rel = dir_path.join(&entry_name);
146-
if is_dir {
147-
dir_queue.push(full_rel);
148-
} else {
149-
let key = full_rel.strip_prefix(source_root).unwrap().to_path_buf();
150-
paths.push(key);
144+
for entry in entries {
145+
match entry {
146+
DirEntry::Dir(name) => dir_queue.push(dir_path.join(name)),
147+
DirEntry::File(name) => {
148+
let full_rel = dir_path.join(name);
149+
let key = full_rel.strip_prefix(source_root).unwrap().to_path_buf();
150+
paths.push(key);
151+
}
151152
}
152153
}
153154
}
@@ -190,14 +191,13 @@ impl DataType for Image {
190191
.list_dir(source_root)
191192
.map_err(|e| StoreEntryError::new(source_root.to_path_buf(), e.into()))?;
192193

193-
for (entry_name, is_dir) in entries {
194-
let full_rel = source_root.join(&entry_name);
195-
if is_dir {
194+
for entry in entries {
195+
match entry {
196196
// The spec forbids directories.
197-
return Err(StoreEntryError::new(full_rel, StoreError::Subdir));
198-
} else {
199-
let key = full_rel.strip_prefix(source_root).unwrap().to_path_buf();
200-
paths.push(key);
197+
DirEntry::Dir(name) => {
198+
return Err(StoreEntryError::new(name, StoreError::Subdir))
199+
}
200+
DirEntry::File(name) => paths.push(name),
201201
}
202202
}
203203

@@ -806,21 +806,18 @@ mod tests {
806806

807807
/// A FontSource backed by in-memory data, with `as_path() -> None`.
808808
/// This triggers the eager-loading branch in `Store::new`.
809+
#[derive(Default)]
809810
struct MemorySource {
810811
files: HashMap<PathBuf, Vec<u8>>,
811-
dirs: HashMap<PathBuf, Vec<(PathBuf, bool)>>,
812+
dirs: HashMap<PathBuf, Vec<DirEntry>>,
812813
}
813814

814815
impl MemorySource {
815-
fn new() -> Self {
816-
MemorySource { files: HashMap::new(), dirs: HashMap::new() }
817-
}
818-
819816
fn add_file(&mut self, path: impl Into<PathBuf>, data: Vec<u8>) {
820817
self.files.insert(path.into(), data);
821818
}
822819

823-
fn add_dir(&mut self, path: impl Into<PathBuf>, entries: Vec<(PathBuf, bool)>) {
820+
fn add_dir(&mut self, path: impl Into<PathBuf>, entries: Vec<DirEntry>) {
824821
self.dirs.insert(path.into(), entries);
825822
}
826823
}
@@ -830,7 +827,7 @@ mod tests {
830827
self.files.get(path).cloned().map(Ok)
831828
}
832829

833-
fn list_dir(&self, path: &Path) -> Result<Vec<(PathBuf, bool)>, std::io::Error> {
830+
fn list_dir(&self, path: &Path) -> Result<Vec<DirEntry>, std::io::Error> {
834831
self.dirs.get(path).cloned().ok_or_else(|| {
835832
std::io::Error::new(std::io::ErrorKind::NotFound, format!("{path:?} not found"))
836833
})
@@ -841,10 +838,10 @@ mod tests {
841838

842839
#[test]
843840
fn data_eager_loading_from_memory_source() {
844-
let mut source = MemorySource::new();
841+
let mut source = MemorySource::default();
845842
source.add_dir(
846843
"data",
847-
vec![(PathBuf::from("a.txt"), false), (PathBuf::from("b.txt"), false)],
844+
vec![DirEntry::File("a.txt".into()), DirEntry::File("b.txt".into())],
848845
);
849846
source.add_file("data/a.txt", b"aaa".to_vec());
850847
source.add_file("data/b.txt", b"bbb".to_vec());
@@ -865,8 +862,8 @@ mod tests {
865862
#[test]
866863
fn image_eager_loading_from_memory_source() {
867864
let img = png_data(b"test");
868-
let mut source = MemorySource::new();
869-
source.add_dir("images", vec![(PathBuf::from("x.png"), false)]);
865+
let mut source = MemorySource::default();
866+
source.add_dir("images", vec![DirEntry::File("x.png".into())]);
870867
source.add_file("images/x.png", img.clone());
871868

872869
let store = ImageStore::new(&source).unwrap();
@@ -879,8 +876,8 @@ mod tests {
879876

880877
#[test]
881878
fn eager_load_invalid_image_fails_at_construction() {
882-
let mut source = MemorySource::new();
883-
source.add_dir("images", vec![(PathBuf::from("bad.png"), false)]);
879+
let mut source = MemorySource::default();
880+
source.add_dir("images", vec![DirEntry::File("bad.png".into())]);
884881
source.add_file("images/bad.png", b"not a png".to_vec());
885882

886883
// Eager loading validates during construction, so this should fail.
@@ -890,10 +887,9 @@ mod tests {
890887

891888
#[test]
892889
fn eager_load_nested_data_from_memory_source() {
893-
let mut source = MemorySource::new();
894-
source
895-
.add_dir("data", vec![(PathBuf::from("top.txt"), false), (PathBuf::from("sub"), true)]);
896-
source.add_dir("data/sub", vec![(PathBuf::from("deep.txt"), false)]);
890+
let mut source = MemorySource::default();
891+
source.add_dir("data", vec![DirEntry::File("top.txt".into()), DirEntry::Dir("sub".into())]);
892+
source.add_dir("data/sub", vec![DirEntry::File("deep.txt".into())]);
897893
source.add_file("data/top.txt", b"top".to_vec());
898894
source.add_file("data/sub/deep.txt", b"deep".to_vec());
899895

src/error.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ pub enum LayerLoadError {
163163
source: GlifLoadError,
164164
},
165165
/// An [`std::io::Error`].
166-
#[error("failed to read layer data")]
166+
#[error("failed to read layer data: '{0}'")]
167167
Io(#[from] IoError),
168168
/// Could not find the layer's contents.plist.
169169
#[error("cannot find the contents.plist file")]
@@ -192,7 +192,7 @@ pub enum FontInfoLoadError {
192192
#[error("fontinfo.plist contains invalid data: {0}")]
193193
InvalidData(FontInfoErrorKind),
194194
/// An [`std::io::Error`].
195-
#[error("failed to read fontinfo.plist file")]
195+
#[error("failed to read fontinfo.plist file: '{0}'")]
196196
Io(#[from] IoError),
197197
/// Could not parse the UFO's fontinfo.plist.
198198
#[error("failed to parse fontinfo.plist file")]
@@ -313,8 +313,11 @@ impl StoreEntryError {
313313
/// Returns `true` if the store could not be populated because the source
314314
/// does not support directory listing or the directory does not exist.
315315
pub(crate) fn is_missing_or_unsupported(&self) -> bool {
316-
matches!(&self.source, StoreError::Io(e)
317-
if matches!(e.kind(), std::io::ErrorKind::Unsupported | std::io::ErrorKind::NotFound))
316+
if let StoreError::Io(e) = &self.source {
317+
matches!(e.kind(), std::io::ErrorKind::Unsupported | std::io::ErrorKind::NotFound)
318+
} else {
319+
false
320+
}
318321
}
319322
}
320323

src/font_source.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,24 @@ use std::path::{Path, PathBuf};
1212
/// Paths passed to methods are always relative to the UFO root, e.g.
1313
/// `"metainfo.plist"`, `"glyphs/contents.plist"`, `"glyphs/A_.glif"`.
1414
///
15-
/// A filesystem directory (a `&Path`) implements this trait directly, so you
16-
/// can pass a path wherever a `FontSource` is expected.
15+
/// Two implementations are provided out of the box:
16+
///
17+
/// - A filesystem directory (a `&Path`) implements this trait directly, so you
18+
/// can pass a path wherever a `FontSource` is expected.
19+
/// - Any closure `Fn(&Path) -> Option<Result<Vec<u8>, io::Error>>` implements it
20+
/// too, which is handy for a quick ad-hoc source without defining a type:
21+
///
22+
/// ```
23+
/// use std::io;
24+
/// use std::path::Path;
25+
/// use norad::{DataRequest, Font};
26+
///
27+
/// # fn example(lookup: impl Fn(&Path) -> Option<Vec<u8>> + Sync) -> Result<(), Box<dyn std::error::Error>> {
28+
/// let source = |path: &Path| lookup(path).map(Ok::<_, io::Error>);
29+
/// let font = Font::load_from_source(&DataRequest::all(), &source)?;
30+
/// # Ok(())
31+
/// # }
32+
/// ```
1733
///
1834
/// # Implementing
1935
///
@@ -60,21 +76,30 @@ pub trait FontSource: Sync {
6076

6177
/// List entries in a directory at the given relative path.
6278
///
63-
/// Returns `(entry_name, is_dir)` pairs, where `entry_name` is the name
64-
/// of each entry (not a full path). Callers should join with the directory
65-
/// path to get the full relative path.
79+
/// Returns a [`DirEntry`] for each entry, carrying the entry name (not a
80+
/// full path). Callers should join with the directory path to get the full
81+
/// relative path.
6682
///
6783
/// The default implementation returns [`io::ErrorKind::Unsupported`],
6884
/// meaning this source does not support directory enumeration. Data and
6985
/// image stores will be empty for such sources.
70-
fn list_dir(&self, _path: &Path) -> Result<Vec<(PathBuf, bool)>, io::Error> {
86+
fn list_dir(&self, _path: &Path) -> Result<Vec<DirEntry>, io::Error> {
7187
Err(io::Error::new(
7288
io::ErrorKind::Unsupported,
7389
"this FontSource does not support directory listing",
7490
))
7591
}
7692
}
7793

94+
/// The name of a file or directory, relative to a parent (not a full path!)
95+
#[derive(Clone, Debug)]
96+
pub enum DirEntry {
97+
/// A file, carrying its name relative to the parent directory.
98+
File(PathBuf),
99+
/// A subdirectory, carrying its name relative to the parent directory.
100+
Dir(PathBuf),
101+
}
102+
78103
/// A directory on disk implements [`FontSource`] directly.
79104
impl FontSource for &Path {
80105
fn try_read(&self, path: &Path) -> Option<Result<Vec<u8>, io::Error>> {
@@ -89,14 +114,18 @@ impl FontSource for &Path {
89114
Some(self)
90115
}
91116

92-
fn list_dir(&self, path: &Path) -> Result<Vec<(PathBuf, bool)>, io::Error> {
117+
fn list_dir(&self, path: &Path) -> Result<Vec<DirEntry>, io::Error> {
93118
let full = self.join(path);
94119
let mut entries = Vec::new();
95120
for entry in std::fs::read_dir(&full)? {
96121
let entry = entry?;
97122
let metadata = entry.metadata()?;
98123
let name = PathBuf::from(entry.file_name());
99-
entries.push((name, metadata.is_dir()));
124+
entries.push(if metadata.is_dir() {
125+
DirEntry::Dir(name)
126+
} else {
127+
DirEntry::File(name)
128+
});
100129
}
101130
Ok(entries)
102131
}

src/fontinfo.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
//!
33
//! [`fontinfo.plist`]: https://unifiedfontobject.org/versions/ufo3/fontinfo.plist/
44
5-
use std::path::Path;
65
use std::{collections::HashSet, convert::TryFrom, ops::Deref};
76

87
use serde::de::Deserializer;
@@ -488,24 +487,6 @@ struct FontInfoV1 {
488487
}
489488

490489
impl FontInfo {
491-
/// Returns [`FontInfo`] from a file, upgrading from the supplied `format_version` to the highest
492-
/// internally supported version.
493-
///
494-
/// The conversion follows what ufoLib and defcon are doing, e.g. various fields that were
495-
/// implicitly signed integers before and are unsigned integers in the newest spec, are
496-
/// converted by taking their absolute value. Fields that could be floats before and are
497-
/// integers now are rounded. Fields that could be floats before and are unsigned integers
498-
/// now are rounded before taking their absolute value.
499-
#[allow(dead_code)]
500-
pub(crate) fn from_file<P: AsRef<Path>>(
501-
path: P,
502-
format_version: FormatVersion,
503-
lib: &mut Plist,
504-
) -> Result<Self, FontInfoLoadError> {
505-
let data = std::fs::read(path.as_ref()).map_err(FontInfoLoadError::Io)?;
506-
Self::from_bytes(&data, format_version, lib)
507-
}
508-
509490
pub(crate) fn from_bytes(
510491
data: &[u8],
511492
format_version: FormatVersion,

src/layer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ impl Layer {
344344
let full_path = layer_dir.join(glyph_path);
345345

346346
source
347-
.read(&full_path)
347+
.read(&full_path)?
348348
.map_err(GlifLoadError::Io)
349349
.and_then(|data| Glyph::parse(&data))
350350
.map_err(|source| LayerLoadError::Glyph {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ mod write;
8787

8888
pub use data_request::DataRequest;
8989
pub use font::{Font, FormatVersion, MetaInfo};
90-
pub use font_source::FontSource;
90+
pub use font_source::{DirEntry, FontSource};
9191
pub use fontinfo::FontInfo;
9292
pub use glyph::{
9393
AffineTransform, Anchor, Codepoints, Component, Contour, ContourPoint, Glyph, Image, PointType,

0 commit comments

Comments
 (0)