Skip to content

Commit

Permalink
Add custom Codepoints type, wrapping IndexSet
Browse files Browse the repository at this point in the history
  • Loading branch information
cmyr committed Dec 5, 2022
1 parent b67c9f4 commit 1ef6f20
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 15 deletions.
90 changes: 90 additions & 0 deletions src/glyph/codepoints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//! A collection of codepoints
//!
//! We want to preserve order and ensure uniqueness, so we use an IndexSet;
//! however we don't want this to be part of our public API, so use a wrapper.

use indexmap::IndexSet;

/// A set of Unicode codepoints
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Codepoints(IndexSet<char>);

impl Codepoints {
/// Construct a new set of codepoints.
///
///
/// The input can be anything that impls `IntoIterator<Item=char>`,
/// and the simplest use would be to pass an array:
///
/// ```
/// # use norad::Codepoints;
/// let mut codepoints = Codepoints::new(['A', 'B']);
/// ```
pub fn new(src: impl IntoIterator<Item = char>) -> Self {
Self(src.into_iter().collect())
}
/// Return the number of codepoints.
pub fn len(&self) -> usize {
self.0.len()
}

/// Returns true if there are no codepoints.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

/// Set the codepoints. See [Codepoints::new] for usage.
pub fn set(&mut self, codepoints: impl IntoIterator<Item = char>) {
self.0.clear();
self.0.extend(codepoints);
}

/// Remove all codepoints from the set.
pub fn clear(&mut self) {
self.0.clear()
}

/// Returns true if the provided codepoint is in this set.
pub fn contains(&self, codepoint: char) -> bool {
self.0.contains(&codepoint)
}

/// Insert a codepoint into the set.
///
/// Returns `true` if this item did not exist in the set.
/// If this item *does* exist, the order will be unchanged.
pub fn insert(&mut self, codepoint: char) -> bool {
self.0.insert(codepoint)
}

/// Iterate over the codepoints.
pub fn iter(&self) -> impl Iterator<Item = char> + '_ {
self.0.iter().copied()
}
}

impl FromIterator<char> for Codepoints {
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
Codepoints(iter.into_iter().collect())
}
}

impl IntoIterator for Codepoints {
type Item = char;

type IntoIter = indexmap::set::IntoIter<char>;

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

impl<'a> IntoIterator for &'a Codepoints {
type Item = &'a char;

type IntoIter = indexmap::set::Iter<'a, char>;

fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
6 changes: 4 additions & 2 deletions src/glyph/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Data related to individual glyphs.

pub mod builder;
mod codepoints;
mod parse;
mod serialize;
#[cfg(test)]
Expand All @@ -13,14 +14,15 @@ use crate::error::ConvertContourError;

#[cfg(feature = "druid")]
use druid::{Data, Lens};
use indexmap::IndexSet;

use crate::error::{ErrorKind, GlifLoadError, GlifWriteError, StoreError};
use crate::name::Name;
use crate::names::NameList;
use crate::shared_types::PUBLIC_OBJECT_LIBS_KEY;
use crate::{Color, Guideline, Identifier, Line, Plist, WriteOptions};

pub use codepoints::Codepoints;

/// A glyph, loaded from a [`.glif` file][glif].
///
/// Norad can load glif version 1.0 and 2.0, and can save 2.0 only.
Expand All @@ -39,7 +41,7 @@ pub struct Glyph {
/// A collection of glyph Unicode code points.
///
/// The first entry defines the primary Unicode value for this glyph.
pub codepoints: IndexSet<char>,
pub codepoints: Codepoints,
/// Arbitrary glyph note.
pub note: Option<String>,
/// A collection of glyph guidelines.
Expand Down
6 changes: 2 additions & 4 deletions src/glyph/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use indexmap::indexset;

use super::parse::parse_glyph;
use super::*;
use crate::write::QuoteChar;
Expand Down Expand Up @@ -883,9 +881,9 @@ fn deduplicate_unicodes2() {
</glyph>
"#;
let mut glyph = parse_glyph(data.as_bytes()).unwrap();
assert_eq!(glyph.codepoints, indexset!['e', 'f', 'g'].into());
assert_eq!(glyph.codepoints, Codepoints::new(['e', 'f', 'g']));

glyph.codepoints = indexset!['e', 'f', 'e', 'g'].into();
glyph.codepoints = Codepoints::new(['e', 'f', 'e', 'g']);
let data2 = glyph.encode_xml().unwrap();
let data2 = std::str::from_utf8(&data2).unwrap();
let data2_expected = r#"<?xml version="1.0" encoding="UTF-8"?>
Expand Down
6 changes: 2 additions & 4 deletions src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,9 +594,7 @@ impl Default for Layer {

#[cfg(test)]
mod tests {
use indexmap::indexset;

use crate::DataRequest;
use crate::{Codepoints, DataRequest};

use super::*;
use std::path::Path;
Expand All @@ -615,7 +613,7 @@ mod tests {
let glyph = layer.get_glyph("A").expect("failed to load glyph 'A'");
assert_eq!(glyph.height, 0.);
assert_eq!(glyph.width, 1190.);
assert_eq!(glyph.codepoints, indexset!['A']);
assert_eq!(glyph.codepoints, Codepoints::new(['A']));
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub use data_request::DataRequest;
pub use font::{Font, FormatVersion, MetaInfo};
pub use fontinfo::FontInfo;
pub use glyph::{
AffineTransform, Anchor, Component, Contour, ContourPoint, Glyph, Image, PointType,
AffineTransform, Anchor, Codepoints, Component, Contour, ContourPoint, Glyph, Image, PointType,
};

pub use name::Name;
Expand Down
7 changes: 3 additions & 4 deletions tests/save.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Testing saving files.

use indexmap::indexset;
use norad::{Font, FormatVersion, Glyph, Identifier, Plist};
use norad::{Codepoints, Font, FormatVersion, Glyph, Identifier, Plist};
use plist::Value;

#[test]
Expand All @@ -25,7 +24,7 @@ fn save_default() {
fn save_new_file() {
let mut my_ufo = Font::new();
let mut my_glyph = Glyph::new("A");
my_glyph.codepoints = indexset!['A'];
my_glyph.codepoints.set(['A']);
my_glyph.note = Some("I did a glyph!".into());
let mut plist = Plist::new();
plist.insert("my-cool-key".into(), plist::Value::Integer(420_u32.into()));
Expand All @@ -43,7 +42,7 @@ fn save_new_file() {
let loaded = Font::load(dir).unwrap();
assert!(loaded.default_layer().get_glyph("A").is_some());
let glyph = loaded.default_layer().get_glyph("A").unwrap();
assert_eq!(glyph.codepoints, indexset!['A']);
assert_eq!(glyph.codepoints, Codepoints::new(['A']));
let lib_val = glyph.lib.get("my-cool-key").and_then(|val| val.as_unsigned_integer());
assert_eq!(lib_val, Some(420));
}
Expand Down

0 comments on commit 1ef6f20

Please sign in to comment.