tinymist_world/font/
profile.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use serde::{Deserialize, Serialize};
use sha2::Digest;
use std::{collections::HashMap, time::SystemTime};
use typst::text::{Coverage, FontInfo};

type FontMetaDict = HashMap<String, String>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FontInfoItem {
    /// customized profile data
    pub meta: FontMetaDict,
    /// The informatioin of the font
    pub info: FontInfo,
}

impl FontInfoItem {
    pub fn new(info: FontInfo) -> Self {
        Self {
            meta: Default::default(),
            info,
        }
    }

    pub fn index(&self) -> Option<u32> {
        self.meta.get("index").and_then(|v| v.parse::<u32>().ok())
    }

    pub fn set_index(&mut self, v: u32) {
        self.meta.insert("index".to_owned(), v.to_string());
    }

    pub fn coverage_hash(&self) -> Option<&String> {
        self.meta.get("coverage_hash")
    }

    pub fn set_coverage_hash(&mut self, v: String) {
        self.meta.insert("coverage_hash".to_owned(), v);
    }

    pub fn meta(&self) -> &FontMetaDict {
        &self.meta
    }

    pub fn info(&self) -> &FontInfo {
        &self.info
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FontProfileItem {
    /// The hash of the file
    pub hash: String,
    /// customized profile data
    pub meta: FontMetaDict,
    /// The informatioin of the font
    pub info: Vec<FontInfoItem>,
}

fn to_micro_lossy(t: SystemTime) -> u128 {
    t.duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_micros()
}

impl FontProfileItem {
    pub fn new(kind: &str, hash: String) -> Self {
        let mut meta: FontMetaDict = Default::default();
        meta.insert("kind".to_owned(), kind.to_string());

        Self {
            hash,
            meta,
            info: Default::default(),
        }
    }

    pub fn path(&self) -> Option<&String> {
        self.meta.get("path")
    }

    pub fn mtime(&self) -> Option<SystemTime> {
        self.meta.get("mtime").and_then(|v| {
            let v = v.parse::<u64>().ok();
            v.map(|v| SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(v))
        })
    }

    pub fn mtime_is_exact(&self, t: SystemTime) -> bool {
        self.mtime()
            .map(|s| {
                let s = to_micro_lossy(s);
                let t = to_micro_lossy(t);
                s == t
            })
            .unwrap_or_default()
    }

    pub fn set_path(&mut self, v: String) {
        self.meta.insert("path".to_owned(), v);
    }

    pub fn set_mtime(&mut self, v: SystemTime) {
        self.meta
            .insert("mtime".to_owned(), to_micro_lossy(v).to_string());
    }

    pub fn hash(&self) -> &str {
        &self.hash
    }

    pub fn meta(&self) -> &FontMetaDict {
        &self.meta
    }

    pub fn info(&self) -> &[FontInfoItem] {
        &self.info
    }

    pub fn add_info(&mut self, info: FontInfoItem) {
        self.info.push(info);
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct FontProfile {
    pub version: String,
    pub build_info: String,
    pub items: Vec<FontProfileItem>,
}

pub fn get_font_coverage_hash(coverage: &Coverage) -> String {
    let mut coverage_hash = sha2::Sha256::new();
    coverage
        .iter()
        .for_each(|c| coverage_hash.update(c.to_le_bytes()));
    let coverage_hash = coverage_hash.finalize();
    format!("sha256:{coverage_hash:x}")
}