Skip to content

Commit

Permalink
Auto merge of #172 - derekdreery:multi-iter, r=jdm
Browse files Browse the repository at this point in the history
Add `iter` to `MultiSource`, and `as_any` to `Source`

The `iter`/`iter_mut` methods allow iterating through the subsources of a `MultiSource`. The `as_any`/`as_mut_any` methods are necessary for downcasting `Source`s into concrete types.

In addition, the `find_source` and `find_source_mut` methods are added as a convenience to find the first source matching the given type.
  • Loading branch information
bors-servo authored Dec 4, 2020
2 parents b181122 + bed0e68 commit da337c4
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 10 deletions.
8 changes: 8 additions & 0 deletions src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,12 @@ pub trait Source: Any {
}
Ok(fields)
}

/// Accesses this `Source` as `Any`, which allows downcasting back to a concrete type from a
/// trait object.
fn as_any(&self) -> &dyn Any;

/// Accesses this `Source` as `Any`, which allows downcasting back to a concrete type from a
/// trait object.
fn as_mut_any(&mut self) -> &mut dyn Any;
}
17 changes: 14 additions & 3 deletions src/sources/core_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ use core_foundation::string::CFString;
use core_text::font_collection::{self, CTFontCollection};
use core_text::font_descriptor::{self, CTFontDescriptor};
use core_text::font_manager;
use std::any::Any;
use std::collections::HashMap;
use std::f32;
use std::fs::File;
use std::path::Path;
use std::sync::Arc;

use crate::error::SelectionError;
use crate::family_handle::FamilyHandle;
Expand All @@ -30,9 +34,6 @@ use crate::loaders::core_text::{self as core_text_loader, FONT_WEIGHT_MAPPING};
use crate::properties::{Properties, Stretch, Weight};
use crate::source::Source;
use crate::utils;
use std::collections::HashMap;
use std::fs::File;
use std::sync::Arc;

/// A source that contains the installed fonts on macOS.
#[allow(missing_debug_implementations)]
Expand Down Expand Up @@ -126,6 +127,16 @@ impl Source for CoreTextSource {
fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> {
self.select_by_postscript_name(postscript_name)
}

#[inline]
fn as_any(&self) -> &dyn Any {
self
}

#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}

#[allow(dead_code)]
Expand Down
11 changes: 11 additions & 0 deletions src/sources/directwrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use dwrote::Font as DWriteFont;
use dwrote::FontCollection as DWriteFontCollection;
use std::any::Any;

use crate::error::SelectionError;
use crate::family_handle::FamilyHandle;
Expand Down Expand Up @@ -123,4 +124,14 @@ impl Source for DirectWriteSource {
fn select_family_by_name(&self, family_name: &str) -> Result<FamilyHandle, SelectionError> {
self.select_family_by_name(family_name)
}

#[inline]
fn as_any(&self) -> &dyn Any {
self
}

#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}
11 changes: 11 additions & 0 deletions src/sources/fontconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::family_name::FamilyName;
use crate::handle::Handle;
use crate::properties::Properties;
use crate::source::Source;
use std::any::Any;

/// A source that contains the fonts installed on the system, as reported by the Fontconfig
/// library.
Expand Down Expand Up @@ -227,6 +228,16 @@ impl Source for FontconfigSource {
fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> {
self.select_by_postscript_name(postscript_name)
}

#[inline]
fn as_any(&self) -> &dyn Any {
self
}

#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}

// A minimal fontconfig wrapper.
Expand Down
11 changes: 11 additions & 0 deletions src/sources/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
//!
//! This is the native source on Android.
use std::any::Any;
use std::fs::File;
use std::path::PathBuf;
use walkdir::WalkDir;
Expand Down Expand Up @@ -141,6 +142,16 @@ impl Source for FsSource {
fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> {
self.select_by_postscript_name(postscript_name)
}

#[inline]
fn as_any(&self) -> &dyn Any {
self
}

#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}

#[cfg(target_os = "android")]
Expand Down
30 changes: 24 additions & 6 deletions src/sources/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::font::Font;
use crate::handle::Handle;
use crate::properties::Properties;
use crate::source::Source;
use std::any::Any;

/// A source that keeps fonts in memory.
#[allow(missing_debug_implementations)]
Expand All @@ -25,6 +26,11 @@ pub struct MemSource {
}

impl MemSource {
/// Creates a new empty memory source.
pub fn empty() -> MemSource {
MemSource { families: vec![] }
}

/// Creates a new memory source that contains the given set of font handles.
///
/// The fonts referenced by the handles are eagerly loaded into memory.
Expand All @@ -42,14 +48,16 @@ impl MemSource {

/// Add an existing font handle to a `MemSource`.
///
/// Returns the font that was just added.
///
/// Note that adding fonts to an existing `MemSource` is slower than creating a new one from a
/// `Handle` iterator, since this method sorts after every addition, rather than once at the
/// end.
pub fn add_font(&mut self, handle: Handle) -> Result<(), FontLoadingError> {
add_font(handle, &mut self.families)?;
pub fn add_font(&mut self, handle: Handle) -> Result<Font, FontLoadingError> {
let font = add_font(handle, &mut self.families)?;
self.families
.sort_by(|a, b| a.family_name.cmp(&b.family_name));
Ok(())
Ok(font)
}

/// Add a number of existing font handles to a `MemSource`.
Expand Down Expand Up @@ -165,10 +173,20 @@ impl Source for MemSource {
fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> {
self.select_by_postscript_name(postscript_name)
}

#[inline]
fn as_any(&self) -> &dyn Any {
self
}

#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}

/// Adds a font, but doesn't sort
fn add_font(handle: Handle, families: &mut Vec<FamilyEntry>) -> Result<(), FontLoadingError> {
/// Adds a font, but doesn't sort. Returns the font that was created to check for validity.
fn add_font(handle: Handle, families: &mut Vec<FamilyEntry>) -> Result<Font, FontLoadingError> {
let font = Font::from_handle(&handle)?;
if let Some(postscript_name) = font.postscript_name() {
families.push(FamilyEntry {
Expand All @@ -177,7 +195,7 @@ fn add_font(handle: Handle, families: &mut Vec<FamilyEntry>) -> Result<(), FontL
font: handle,
})
}
Ok(())
Ok(font)
}

struct FamilyEntry {
Expand Down
75 changes: 74 additions & 1 deletion src/sources/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ use crate::family_name::FamilyName;
use crate::handle::Handle;
use crate::properties::Properties;
use crate::source::Source;
use std::ops::{Index, IndexMut};
use std::{
any::Any,
fmt,
ops::{Index, IndexMut},
slice,
};

/// A source that encapsulates multiple sources and allows them to be queried as a group.
///
Expand Down Expand Up @@ -91,6 +96,32 @@ impl MultiSource {
) -> Result<Handle, SelectionError> {
<Self as Source>::select_best_match(self, family_names, properties)
}

/// Returns an iterator over the contained sources.
#[inline]
pub fn iter<'a>(&'a self) -> MultiIter<'a> {
MultiIter(self.subsources.iter())
}

/// Returns an iterator over the contained sources with mutable access.
#[inline]
pub fn iter_mut<'a>(&'a mut self) -> MultiIterMut<'a> {
MultiIterMut(self.subsources.iter_mut())
}

/// A convenience method to get the first source with the given type.
///
/// Returns `None` if no source of the given type was found.
pub fn find_source<T: Source + 'static>(&self) -> Option<&T> {
self.iter().find_map(|v| v.as_any().downcast_ref())
}

/// A convenience method to get the first source with the given type.
///
/// Returns `None` if no source of the given type was found.
pub fn find_source_mut<T: Source + 'static>(&mut self) -> Option<&mut T> {
self.iter_mut().find_map(|v| v.as_mut_any().downcast_mut())
}
}

impl Source for MultiSource {
Expand All @@ -113,6 +144,14 @@ impl Source for MultiSource {
fn select_by_postscript_name(&self, postscript_name: &str) -> Result<Handle, SelectionError> {
self.select_by_postscript_name(postscript_name)
}

fn as_any(&self) -> &dyn Any {
self
}

fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}

impl Index<usize> for MultiSource {
Expand All @@ -128,3 +167,37 @@ impl IndexMut<usize> for MultiSource {
&mut *self.subsources[idx]
}
}

/// An iterator over the sources in a [`MultiSource`].
pub struct MultiIter<'a>(slice::Iter<'a, Box<dyn Source>>);

impl<'a> Iterator for MultiIter<'a> {
type Item = &'a dyn Source;

fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|v| &**v)
}
}

impl fmt::Debug for MultiIter<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MultiIter").finish()
}
}

/// An iterator over the mutable sources in a [`MultiSource`].
pub struct MultiIterMut<'a>(slice::IterMut<'a, Box<dyn Source>>);

impl<'a> Iterator for MultiIterMut<'a> {
type Item = &'a mut dyn Source;

fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|v| &mut **v)
}
}

impl fmt::Debug for MultiIterMut<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MultiIterMut").finish()
}
}

0 comments on commit da337c4

Please sign in to comment.