hdiff_update_core/
manifest.rs1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{Error, Result};
6
7pub const DEFAULT_ALGORITHM_NAME: &str = "hdiffpatch-v5-window-zstd";
8pub const MANIFEST_SIGNATURE_ALGORITHM: &str = "ed25519";
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct Artifact {
13 pub url: String,
14 pub sha256: String,
15 pub size: u64,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub signature: Option<String>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[serde(rename_all = "camelCase")]
22pub struct DeltaArtifact {
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub from_version: Option<String>,
25 pub from_sha256: String,
26 pub url: String,
27 pub sha256: String,
28 pub size: u64,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub target_sha256: Option<String>,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub target_size: Option<u64>,
33 #[serde(default = "default_algorithm")]
34 pub algorithm: String,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub struct ManifestSignature {
40 #[serde(default = "default_signature_algorithm")]
41 pub algorithm: String,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub key_id: Option<String>,
44 pub value: String,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct PlatformRelease {
50 pub full: Artifact,
51 #[serde(default)]
52 pub deltas: Vec<DeltaArtifact>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56#[serde(rename_all = "camelCase")]
57pub struct UpdateManifest {
58 pub version: String,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
60 pub notes: Option<String>,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub published_at: Option<String>,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub signature: Option<ManifestSignature>,
65 pub platforms: BTreeMap<String, PlatformRelease>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(tag = "kind", rename_all = "camelCase")]
70pub enum SelectedArtifact {
71 Delta { artifact: DeltaArtifact },
72 Full { artifact: Artifact },
73}
74
75impl UpdateManifest {
76 pub fn signing_payload(&self) -> Result<Vec<u8>> {
77 let mut unsigned = self.clone();
78 unsigned.signature = None;
79 Ok(serde_json::to_vec(&unsigned)?)
80 }
81}
82
83impl UpdateManifest {
84 pub fn platform(&self, platform: &str) -> Result<&PlatformRelease> {
85 self.platforms
86 .get(platform)
87 .ok_or_else(|| Error::MissingPlatform {
88 platform: platform.to_string(),
89 })
90 }
91
92 pub fn select_artifact(
93 &self,
94 platform: &str,
95 current_version: Option<&str>,
96 current_sha256: Option<&str>,
97 ) -> Result<SelectedArtifact> {
98 let platform_release = self.platform(platform)?;
99
100 if let Some(current_sha256) = current_sha256 {
101 if let Some(delta) = platform_release.deltas.iter().find(|delta| {
102 let hash_matches = delta.from_sha256.eq_ignore_ascii_case(current_sha256);
103 let version_matches = match (&delta.from_version, current_version) {
104 (Some(from), Some(current)) => from == current,
105 (Some(_), None) => true,
106 (None, _) => true,
107 };
108 hash_matches && version_matches
109 }) {
110 return Ok(SelectedArtifact::Delta {
111 artifact: delta.clone(),
112 });
113 }
114 }
115
116 Ok(SelectedArtifact::Full {
117 artifact: platform_release.full.clone(),
118 })
119 }
120}
121
122pub fn default_platform() -> String {
123 let os = if cfg!(target_os = "windows") {
124 "windows"
125 } else if cfg!(target_os = "macos") {
126 "darwin"
127 } else if cfg!(target_os = "linux") {
128 "linux"
129 } else {
130 std::env::consts::OS
131 };
132
133 let arch = if cfg!(target_arch = "x86_64") {
134 "x86_64"
135 } else if cfg!(target_arch = "aarch64") {
136 "aarch64"
137 } else {
138 std::env::consts::ARCH
139 };
140
141 format!("{os}-{arch}")
142}
143
144fn default_algorithm() -> String {
145 DEFAULT_ALGORITHM_NAME.to_string()
146}
147
148fn default_signature_algorithm() -> String {
149 MANIFEST_SIGNATURE_ALGORITHM.to_string()
150}