tinymist_world/font/
profile.rs1use serde::{Deserialize, Serialize};
4use sha2::Digest;
5use std::{collections::HashMap, time::SystemTime};
6use typst::text::{Coverage, FontInfo};
7
8type FontMetaDict = HashMap<String, String>;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct FontInfoItem {
14 pub meta: FontMetaDict,
16 pub info: FontInfo,
18}
19
20impl FontInfoItem {
21 pub fn new(info: FontInfo) -> Self {
23 Self {
24 meta: Default::default(),
25 info,
26 }
27 }
28
29 pub fn index(&self) -> Option<u32> {
31 self.meta.get("index").and_then(|v| v.parse::<u32>().ok())
32 }
33
34 pub fn set_index(&mut self, v: u32) {
36 self.meta.insert("index".to_owned(), v.to_string());
37 }
38
39 pub fn coverage_hash(&self) -> Option<&String> {
41 self.meta.get("coverage_hash")
42 }
43
44 pub fn set_coverage_hash(&mut self, v: String) {
46 self.meta.insert("coverage_hash".to_owned(), v);
47 }
48
49 pub fn meta(&self) -> &FontMetaDict {
51 &self.meta
52 }
53
54 pub fn info(&self) -> &FontInfo {
56 &self.info
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct FontProfileItem {
63 pub hash: String,
65 pub meta: FontMetaDict,
67 pub info: Vec<FontInfoItem>,
69}
70
71fn to_micro_lossy(t: SystemTime) -> u128 {
73 t.duration_since(SystemTime::UNIX_EPOCH)
74 .unwrap()
75 .as_micros()
76}
77
78impl FontProfileItem {
79 pub fn new(kind: &str, hash: String) -> Self {
81 let mut meta: FontMetaDict = Default::default();
82 meta.insert("kind".to_owned(), kind.to_string());
83
84 Self {
85 hash,
86 meta,
87 info: Default::default(),
88 }
89 }
90
91 pub fn path(&self) -> Option<&String> {
93 self.meta.get("path")
94 }
95
96 pub fn mtime(&self) -> Option<SystemTime> {
98 self.meta.get("mtime").and_then(|v| {
99 let v = v.parse::<u64>().ok();
100 v.map(|v| SystemTime::UNIX_EPOCH + tinymist_std::time::Duration::from_micros(v))
101 })
102 }
103
104 pub fn mtime_is_exact(&self, t: SystemTime) -> bool {
106 self.mtime()
107 .map(|s| {
108 let s = to_micro_lossy(s);
109 let t = to_micro_lossy(t);
110 s == t
111 })
112 .unwrap_or_default()
113 }
114
115 pub fn set_path(&mut self, v: String) {
117 self.meta.insert("path".to_owned(), v);
118 }
119
120 pub fn set_mtime(&mut self, v: SystemTime) {
122 self.meta
123 .insert("mtime".to_owned(), to_micro_lossy(v).to_string());
124 }
125
126 pub fn hash(&self) -> &str {
128 &self.hash
129 }
130
131 pub fn meta(&self) -> &FontMetaDict {
133 &self.meta
134 }
135
136 pub fn info(&self) -> &[FontInfoItem] {
138 &self.info
139 }
140
141 pub fn add_info(&mut self, info: FontInfoItem) {
143 self.info.push(info);
144 }
145}
146
147#[derive(Default, Debug, Clone, Serialize, Deserialize)]
149pub struct FontProfile {
150 pub version: String,
152 pub build_info: String,
154 pub items: Vec<FontProfileItem>,
156}
157
158pub fn get_font_coverage_hash(coverage: &Coverage) -> String {
160 let mut coverage_hash = sha2::Sha256::new();
161 coverage
162 .iter()
163 .for_each(|c| coverage_hash.update(c.to_le_bytes()));
164 let coverage_hash = coverage_hash.finalize();
165 format!("sha256:{coverage_hash:x}")
166}