1use std::hash::{Hash, Hasher};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub enum CanonicalIdentityRole {
8 Pack,
9 PackageTree,
10 FontContainer,
11 Compilation,
12 CompilationResult,
13}
14
15impl CanonicalIdentityRole {
16 pub const fn as_str(self) -> &'static str {
18 match self {
19 Self::Pack => "pack",
20 Self::PackageTree => "complete-package-tree",
21 Self::FontContainer => "font-container",
22 Self::Compilation => "compilation",
23 Self::CompilationResult => "compilation-result",
24 }
25 }
26
27 pub(crate) const fn schema(self) -> &'static str {
28 match self {
29 Self::Pack => "typst-pack-identity-v1",
30 Self::PackageTree => "typst-pack-complete-package-tree-v1",
31 Self::FontContainer => "typst-pack-font-container-identity-v1",
32 Self::Compilation => "typst-pack-compilation-v1",
33 Self::CompilationResult => "typst-pack-compilation-result-v1",
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
42pub struct CanonicalIdentity {
43 role: CanonicalIdentityRole,
44 digest: u128,
45}
46
47impl Hash for CanonicalIdentity {
48 fn hash<H: Hasher>(&self, state: &mut H) {
49 self.digest.hash(state);
51 }
52}
53
54impl CanonicalIdentity {
55 pub(crate) const fn from_digest(role: CanonicalIdentityRole, digest: u128) -> Self {
56 Self { role, digest }
57 }
58
59 pub fn for_font_container_bytes(data: &[u8]) -> Self {
61 Self::from_digest(
62 CanonicalIdentityRole::FontContainer,
63 typst::utils::hash128(&data),
64 )
65 }
66
67 pub const fn role(self) -> CanonicalIdentityRole {
69 self.role
70 }
71
72 pub const fn schema(self) -> &'static str {
74 self.role.schema()
75 }
76
77 pub const fn algorithm(self) -> &'static str {
79 "typst-hash128-0.15"
80 }
81
82 pub const fn digest(self) -> [u8; 16] {
84 self.digest.to_be_bytes()
85 }
86
87 pub(crate) const fn digest_value(self) -> u128 {
88 self.digest
89 }
90
91 pub(crate) fn encode(self) -> String {
92 format!("{:032x}", self.digest)
93 }
94
95 pub(crate) fn decode(role: CanonicalIdentityRole, value: &str) -> Option<Self> {
96 (value.len() == 32)
97 .then(|| u128::from_str_radix(value, 16).ok())
98 .flatten()
99 .map(|digest| Self::from_digest(role, digest))
100 }
101}
102
103impl std::fmt::Display for CanonicalIdentityRole {
104 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 formatter.write_str(self.as_str())
106 }
107}
108
109impl std::fmt::Display for CanonicalIdentity {
115 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 write!(formatter, "{}:{:032x}", self.role.as_str(), self.digest)
117 }
118}