1use std::hash::Hash;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
6pub struct Font {
7 pub family: Family,
9 pub weight: Weight,
11 pub stretch: Stretch,
13 pub style: Style,
15}
16
17impl Font {
18 pub const DEFAULT: Font = Font {
20 family: Family::SansSerif,
21 weight: Weight::Normal,
22 stretch: Stretch::Normal,
23 style: Style::Normal,
24 };
25
26 pub const MONOSPACE: Font = Font {
28 family: Family::Monospace,
29 ..Self::DEFAULT
30 };
31
32 pub const fn new(name: &'static str) -> Self {
34 Self {
35 family: Family::Name(name),
36 ..Self::DEFAULT
37 }
38 }
39
40 pub fn with_family(family: impl Into<Family>) -> Self {
42 Font {
43 family: family.into(),
44 ..Self::DEFAULT
45 }
46 }
47
48 pub const fn weight(self, weight: Weight) -> Self {
50 Self { weight, ..self }
51 }
52
53 pub const fn stretch(self, stretch: Stretch) -> Self {
55 Self { stretch, ..self }
56 }
57
58 pub const fn style(self, style: Style) -> Self {
60 Self { style, ..self }
61 }
62}
63
64impl From<&'static str> for Font {
65 fn from(name: &'static str) -> Self {
66 Font::new(name)
67 }
68}
69
70impl From<Family> for Font {
71 fn from(family: Family) -> Self {
72 Font::with_family(family)
73 }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
78pub enum Family {
79 Name(&'static str),
81
82 Serif,
84
85 #[default]
89 SansSerif,
90
91 Cursive,
95
96 Fantasy,
99
100 Monospace,
103}
104
105impl Family {
106 pub const VARIANTS: &[Self] = &[
108 Self::Serif,
109 Self::SansSerif,
110 Self::Cursive,
111 Self::Fantasy,
112 Self::Monospace,
113 ];
114
115 pub fn name(name: &str) -> Self {
119 use rustc_hash::FxHashSet;
120 use std::sync::{LazyLock, Mutex};
121
122 static NAMES: LazyLock<Mutex<FxHashSet<&'static str>>> = LazyLock::new(Mutex::default);
123
124 let mut names = NAMES.lock().expect("lock font name cache");
125
126 let Some(name) = names.get(name) else {
127 let name: &'static str = name.to_owned().leak();
128 let _ = names.insert(name);
129
130 return Self::Name(name);
131 };
132
133 Self::Name(name)
134 }
135}
136
137impl From<&str> for Family {
138 fn from(name: &str) -> Self {
139 Family::name(name)
140 }
141}
142
143impl std::fmt::Display for Family {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 f.write_str(match self {
146 Family::Name(name) => name,
147 Family::Serif => "Serif",
148 Family::SansSerif => "Sans-serif",
149 Family::Cursive => "Cursive",
150 Family::Fantasy => "Fantasy",
151 Family::Monospace => "Monospace",
152 })
153 }
154}
155
156#[allow(missing_docs)]
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
159pub enum Weight {
160 Thin,
161 ExtraLight,
162 Light,
163 #[default]
164 Normal,
165 Medium,
166 Semibold,
167 Bold,
168 ExtraBold,
169 Black,
170}
171
172#[allow(missing_docs)]
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
175pub enum Stretch {
176 UltraCondensed,
177 ExtraCondensed,
178 Condensed,
179 SemiCondensed,
180 #[default]
181 Normal,
182 SemiExpanded,
183 Expanded,
184 ExtraExpanded,
185 UltraExpanded,
186}
187
188#[allow(missing_docs)]
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
191pub enum Style {
192 #[default]
193 Normal,
194 Italic,
195 Oblique,
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200pub enum Error {}