1use std::sync::LazyLock;
2
3use topcoat_core::fnv1a::Fnv1a;
4
5use crate::FontFaces;
6
7#[derive(Debug, Clone, PartialEq)]
10pub struct FontData {
11 family: String,
12 faces: FontFaces,
13 hash: u64,
14}
15
16impl FontData {
17 #[must_use]
23 #[track_caller]
24 pub fn new(family: impl Into<String>, faces: impl TryInto<FontFaces>) -> Self {
25 let family = family.into();
26 let faces = faces
27 .try_into()
28 .unwrap_or_else(|_| panic!("font faces must not be empty"));
29 let h = Fnv1a::<u64>::new().write(family.as_bytes()).write(b"\0");
30 let hash = faces.hash(h).finish();
31 Self {
32 family,
33 faces,
34 hash,
35 }
36 }
37
38 #[must_use]
40 pub fn family(&self) -> &str {
41 &self.family
42 }
43
44 #[must_use]
46 pub fn faces(&self) -> &FontFaces {
47 &self.faces
48 }
49
50 #[must_use]
52 pub fn hash(&self) -> u64 {
53 self.hash
54 }
55}
56
57#[derive(Debug, Clone, Copy)]
65pub struct Font(&'static LazyLock<FontData>);
66
67impl Font {
68 #[must_use]
70 pub const fn new(data: &'static LazyLock<FontData>) -> Self {
71 Self(data)
72 }
73
74 #[must_use]
76 pub fn family(&self) -> &str {
77 self.0.family()
78 }
79
80 #[must_use]
82 pub fn faces(&self) -> &FontFaces {
83 self.0.faces()
84 }
85
86 #[must_use]
92 pub fn hash(&self) -> u64 {
93 self.0.hash()
94 }
95}
96
97impl PartialEq for Font {
98 fn eq(&self, other: &Self) -> bool {
99 std::ptr::eq(self.0, other.0)
100 }
101}
102
103impl Eq for Font {}
104
105#[cfg(feature = "discover")]
106inventory::collect!(Font);
107
108#[doc(hidden)]
114#[cfg(feature = "discover")]
115#[macro_export]
116macro_rules! register_font {
117 ($font:expr) => {
118 $crate::internal::inventory::submit! { $font }
119 };
120}
121
122#[doc(hidden)]
123#[cfg(not(feature = "discover"))]
124#[macro_export]
125macro_rules! register_font {
126 ($font:expr) => {};
127}