1use std::cmp::Ordering;
2use std::fmt::{Debug, Display, Formatter, Write};
3use std::str::FromStr;
4use crate::Error;
5
6#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
7pub enum DefinedFontWeight {
8 Fixed(u32),
9 Variable
10}
11impl DefinedFontWeight {
12 pub const REGULAR: Self = DefinedFontWeight::Fixed(400);
13 pub fn is_covered_by(&self, other: &DefinedFontWeight) -> bool {
14 match other {
15 DefinedFontWeight::Fixed(other) => match self {
16 DefinedFontWeight::Fixed(me) => *other == *me,
17 _ => false
18 },
19 DefinedFontWeight::Variable => *self == DefinedFontWeight::Variable
20 }
21 }
22 pub fn is_fixed(&self) -> bool {
23 match self {
24 DefinedFontWeight::Fixed(_) => true,
25 DefinedFontWeight::Variable => false
26 }
27 }
28}
29impl Display for DefinedFontWeight {
30 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31 match self {
32 DefinedFontWeight::Fixed(w) => Display::fmt(w, f),
33 DefinedFontWeight::Variable => f.write_str("variable")
34 }
35 }
36}
37
38impl Ord for DefinedFontWeight {
39 fn cmp(&self, other: &Self) -> Ordering {
45 match self {
46 Self::Variable => match other {
47 Self::Variable => Ordering::Equal,
48 Self::Fixed(_) => Ordering::Less
49 },
50 Self::Fixed(400) => match other {
51 Self::Variable => Ordering::Greater,
52 Self::Fixed(400) => Ordering::Equal,
53 Self::Fixed(_) => Ordering::Less
54 },
55 Self::Fixed(self_w) => match other {
56 Self::Variable => Ordering::Greater,
57 Self::Fixed(400) => Ordering::Greater,
58 Self::Fixed(other_w) => self_w.cmp(other_w)
59 }
60 }
61 }
62}
63impl PartialOrd<Self> for DefinedFontWeight {
64 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
65 Some(self.cmp(other))
66 }
67}
68
69#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
70pub enum FontWeight {
71 Defined(DefinedFontWeight),
72 AllFixed, All, }
75impl FontWeight {
76 pub fn is_covered_by(&self, other: &FontWeight) -> bool {
77 match other {
78 FontWeight::All => true, FontWeight::AllFixed => match self {
80 FontWeight::AllFixed | FontWeight::Defined(DefinedFontWeight::Fixed(_)) => true,
82 _ => false
83 },
84 FontWeight::Defined(other_defined) => match self {
85 FontWeight::Defined(self_defined) => self_defined.is_covered_by(other_defined),
86 _ => false
87 }
88 }
89 }
90}
91
92#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash, Ord, PartialOrd)]
93pub enum DefinedFontStyle {
94 Regular,
95 Italic
96}
97impl DefinedFontStyle {
98 pub fn is_covered_by(&self, other: &DefinedFontStyle) -> bool {
99 self.eq(other)
101 }
102}
103impl FromStr for DefinedFontStyle {
104 type Err = Error;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 if s.is_empty() {
108 return Ok(Self::Regular)
109 }
110 match s {
111 "regular" | "normal" => Ok(Self::Regular),
112 "italic" => Ok(Self::Italic),
113 _ => Err(Error::Deserialisation(format!("No such font style: {}", s)))
114 }
115 }
116}
117impl AsRef<str> for DefinedFontStyle {
118 fn as_ref(&self) -> &str {
119 match self {
120 Self::Regular => "regular",
121 Self::Italic => "italic"
122 }
123 }
124}
125impl Display for DefinedFontStyle {
126 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
127 f.write_str(self.as_ref())
128 }
129}
130
131#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
132pub enum FontStyle {
133 Defined(DefinedFontStyle),
134 All
135}
136
137impl FontStyle {
138 pub fn is_covered_by(&self, other: &FontStyle) -> bool {
139 match other {
140 FontStyle::All => true,
141 FontStyle::Defined(other_defined) => match self {
142 FontStyle::Defined(self_defined) => self_defined.is_covered_by(other_defined),
143 _ => false
144 }
145 }
146 }
147}
148
149#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
150pub struct DefinedFontVariantSpec {
151 pub weight: DefinedFontWeight,
152 pub style: DefinedFontStyle
153}
154
155impl DefinedFontVariantSpec {
156 pub const REGULAR: Self = DefinedFontVariantSpec {
157 weight: DefinedFontWeight::REGULAR,
158 style: DefinedFontStyle::Regular
159 };
160 pub fn is_covered_by(&self, other: &DefinedFontVariantSpec) -> bool {
161 let weight_is_covered = self.weight.is_covered_by(&other.weight);
162 let style_is_covered = self.style.is_covered_by(&other.style);
163 weight_is_covered && style_is_covered
164 }
165}
166
167impl Ord for DefinedFontVariantSpec {
168 fn cmp(&self, other: &Self) -> Ordering {
169 self.weight.cmp(&other.weight)
170 .then_with(|| self.style.cmp(&other.style))
171 }
172}
173
174impl PartialOrd<Self> for DefinedFontVariantSpec {
175 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
176 Some(self.cmp(other))
177 }
178}
179
180#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
181pub struct FontVariantSpec {
182 pub weight: FontWeight,
183 pub style: FontStyle
184}
185
186impl FontVariantSpec {
187 pub fn is_covered_by(&self, other: &FontVariantSpec) -> bool {
188 let weight_is_covered = self.weight.is_covered_by(&other.weight);
189 let style_is_covered = self.style.is_covered_by(&other.style);
190 weight_is_covered && style_is_covered
191 }
192}
193
194#[derive(Eq, PartialEq, Clone, Debug, Hash)]
195pub struct DefinedFontInstallSpec {
196 pub id: String,
197 pub styles: Vec<DefinedFontVariantSpec>
198}
199impl DefinedFontInstallSpec {
200 pub fn new<S, I, F>(id: S, styles: I) -> Self where S: ToString, I: IntoIterator<Item = F>, F: Into<DefinedFontVariantSpec> {
201 Self {
202 id: id.to_string(),
203 styles: styles.into_iter().map(|v| v.into()).collect()
204 }
205 }
206}
207
208#[derive(Eq, PartialEq, Clone, Debug, Hash)]
209pub struct FontInstallSpec {
210 pub id: String,
211 pub styles: Vec<FontVariantSpec>
212}
213
214impl FontInstallSpec {
215 pub fn new<S, I, F>(id: S, styles: I) -> Self where S: ToString, I: IntoIterator<Item = F>, F: Into<FontVariantSpec> {
216 Self {
217 id: id.to_string(),
218 styles: styles.into_iter().map(|v| v.into()).collect()
219 }
220 }
221 pub fn new_all_styles<S>(id: S) -> Self where S: ToString {
222 Self::new(id, vec![FontVariantSpec { style: FontStyle::All, weight: FontWeight::All }])
223 }
224}
225#[derive(Eq, PartialEq, Clone, Debug, Hash)]
226pub struct FontDescription {
227 pub name: String,
228 pub id: String,
229 pub version: String
230}
231impl FontDescription {
232 pub fn new(name: impl ToString, id: impl ToString, version: impl ToString) -> Self {
233 Self {
234 name: name.to_string(),
235 id: id.to_string(),
236 version: version.to_string()
237 }
238 }
239}
240impl AsRef<FontDescription> for FontDescription {
241 fn as_ref(&self) -> &Self {
242 self
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn weight_ordering() {
252 use DefinedFontWeight as W;
253 assert!(W::Fixed(100) < W::Fixed(200));
254 assert!(W::Fixed(200) < W::Fixed(300));
255 assert!(W::Fixed(900) > W::Fixed(800));
256 assert!(W::Fixed(800) > W::Fixed(700));
257 assert!(W::Variable < W::Fixed(100));
259 assert!(W::Variable < W::Fixed(300));
260 assert!(W::Variable < W::Fixed(500));
261 assert!(W::Variable < W::Fixed(700));
262 assert!(W::Variable < W::Fixed(900));
263 assert!(W::Variable < W::REGULAR);
264 assert!(W::REGULAR < W::Fixed(900));
267 assert!(W::REGULAR < W::Fixed(800));
268 assert!(W::REGULAR < W::Fixed(700));
269 assert!(W::REGULAR < W::Fixed(600));
270 assert!(W::REGULAR < W::Fixed(500));
271 assert!(W::REGULAR < W::Fixed(300));
273 assert!(W::REGULAR < W::Fixed(200));
274 assert!(W::REGULAR < W::Fixed(100));
275 assert!(W::Fixed(900) > W::Fixed(400));
277 assert!(W::Fixed(800) > W::Fixed(400));
278 assert!(W::Fixed(700) > W::Fixed(400));
279 assert!(W::Fixed(600) > W::Fixed(400));
280 assert!(W::Fixed(500) > W::Fixed(400));
281 assert!(W::Fixed(300) > W::Fixed(400));
282 assert!(W::Fixed(200) > W::Fixed(400));
283 assert!(W::Fixed(100) > W::Fixed(400));
284 }
285}