ferroui_base/media/fonts/
font_collection_key.rs1use std::cmp::Ordering;
2
3use crate::media::{FontStretch, FontStyle, FontWeight, GlyphTypeface, IPlatformTypeface, Typeface};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub struct FontCollectionKey {
11 pub style: FontStyle,
12 pub weight: FontWeight,
13 pub stretch: FontStretch,
14}
15
16impl FontCollectionKey {
17 pub const fn new(style: FontStyle, weight: FontWeight, stretch: FontStretch) -> Self {
18 Self { style, weight, stretch }
19 }
20}
21
22impl Ord for FontCollectionKey {
23 fn cmp(&self, other: &Self) -> Ordering {
24 (self.style as i32)
25 .cmp(&(other.style as i32))
26 .then_with(|| self.weight.value().cmp(&other.weight.value()))
27 .then_with(|| (self.stretch as i32).cmp(&(other.stretch as i32)))
28 }
29}
30
31impl PartialOrd for FontCollectionKey {
32 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
33 Some(self.cmp(other))
34 }
35}
36
37impl From<&Typeface> for FontCollectionKey {
40 fn from(typeface: &Typeface) -> Self {
41 FontCollectionKey::new(typeface.style(), typeface.weight(), typeface.stretch())
42 }
43}
44
45impl From<&GlyphTypeface> for FontCollectionKey {
46 fn from(glyph_typeface: &GlyphTypeface) -> Self {
47 FontCollectionKey::new(glyph_typeface.style(), glyph_typeface.weight(), glyph_typeface.stretch())
48 }
49}
50
51impl From<&dyn IPlatformTypeface> for FontCollectionKey {
52 fn from(platform_typeface: &dyn IPlatformTypeface) -> Self {
53 FontCollectionKey::new(platform_typeface.style(), platform_typeface.weight(), platform_typeface.stretch())
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn equal_keys_compare_equal() {
63 let a = FontCollectionKey::new(FontStyle::Italic, FontWeight::Bold, FontStretch::Condensed);
64 let b = FontCollectionKey::new(FontStyle::Italic, FontWeight::Bold, FontStretch::Condensed);
65
66 assert_eq!(a.cmp(&b), Ordering::Equal);
67 assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
68 assert!(a <= b);
69 assert!(a >= b);
70 }
71
72 #[test]
73 fn style_is_the_primary_sort_key() {
74 let normal = FontCollectionKey::new(FontStyle::Normal, FontWeight::Black, FontStretch::UltraExpanded);
76 let italic = FontCollectionKey::new(FontStyle::Italic, FontWeight::Thin, FontStretch::UltraCondensed);
78
79 assert_eq!(normal.cmp(&italic), Ordering::Less);
80 assert_eq!(italic.cmp(&normal), Ordering::Greater);
81 assert!(normal < italic);
82 }
83
84 #[test]
85 fn weight_is_the_secondary_sort_key_when_style_matches() {
86 let light = FontCollectionKey::new(FontStyle::Normal, FontWeight::Light, FontStretch::UltraExpanded);
87 let bold = FontCollectionKey::new(FontStyle::Normal, FontWeight::Bold, FontStretch::UltraCondensed);
88
89 assert_eq!(light.cmp(&bold), Ordering::Less);
90 assert!(light < bold);
91 }
92
93 #[test]
94 fn stretch_is_the_tertiary_sort_key_when_style_and_weight_match() {
95 let condensed = FontCollectionKey::new(FontStyle::Normal, FontWeight::Normal, FontStretch::Condensed);
96 let expanded = FontCollectionKey::new(FontStyle::Normal, FontWeight::Normal, FontStretch::Expanded);
97
98 assert_eq!(condensed.cmp(&expanded), Ordering::Less);
99 assert!(condensed < expanded);
100 }
101
102 #[test]
103 fn array_sort_produces_lexicographic_order() {
104 let mut keys = [
105 FontCollectionKey::new(FontStyle::Italic, FontWeight::Normal, FontStretch::Normal),
106 FontCollectionKey::new(FontStyle::Normal, FontWeight::Bold, FontStretch::Normal),
107 FontCollectionKey::new(FontStyle::Normal, FontWeight::Normal, FontStretch::Expanded),
108 FontCollectionKey::new(FontStyle::Normal, FontWeight::Normal, FontStretch::Normal),
109 ];
110
111 keys.sort();
112
113 assert_eq!(
114 keys,
115 [
116 FontCollectionKey::new(FontStyle::Normal, FontWeight::Normal, FontStretch::Normal),
117 FontCollectionKey::new(FontStyle::Normal, FontWeight::Normal, FontStretch::Expanded),
118 FontCollectionKey::new(FontStyle::Normal, FontWeight::Bold, FontStretch::Normal),
119 FontCollectionKey::new(FontStyle::Italic, FontWeight::Normal, FontStretch::Normal),
120 ]
121 );
122 }
123
124 #[test]
125 fn compare_to_is_consistent_with_equality() {
126 let a = FontCollectionKey::new(FontStyle::Oblique, FontWeight::Medium, FontStretch::SemiExpanded);
127 let b = FontCollectionKey::new(FontStyle::Oblique, FontWeight::Medium, FontStretch::SemiExpanded);
128 let c = FontCollectionKey::new(FontStyle::Oblique, FontWeight::Medium, FontStretch::SemiCondensed);
129
130 assert_eq!(a, b);
131 assert_eq!(a.cmp(&b), Ordering::Equal);
132 assert_ne!(a, c);
133 assert_ne!(a.cmp(&c), Ordering::Equal);
134 }
135
136 #[test]
137 fn keys_are_made_from_typefaces() {
138 let typeface = Typeface::from_name_with_style("A", FontStyle::Italic, FontWeight::Bold, FontStretch::Expanded);
139
140 assert_eq!(
141 FontCollectionKey::from(&typeface),
142 FontCollectionKey::new(FontStyle::Italic, FontWeight::Bold, FontStretch::Expanded)
143 );
144 }
145}