1use http::HeaderMap;
4use semver::Version;
5use serde::{Deserialize, Deserializer, Serialize, de::Error as DeError};
6use std::{collections::HashMap, ffi::OsString, path::PathBuf, time::Duration};
7use time::OffsetDateTime;
8use url::Url;
9
10use crate::InstallerKind;
11
12#[derive(Debug, Deserialize, Serialize, Clone)]
14pub struct ReleaseManifestPlatform {
15 pub url: Url,
17 pub signature: String,
19}
20
21#[derive(Debug, Deserialize, Serialize, Clone)]
23#[serde(untagged)]
24pub enum RemoteReleaseInner {
25 Dynamic(ReleaseManifestPlatform),
28 Static {
30 platforms: HashMap<String, ReleaseManifestPlatform>,
32 },
33}
34
35#[derive(Debug, Serialize, Clone)]
37pub struct RemoteRelease {
38 pub version: Version,
40 pub notes: Option<String>,
42 pub pub_date: Option<OffsetDateTime>,
44 #[serde(flatten)]
46 pub data: RemoteReleaseInner,
47 #[serde(skip)]
49 pub download_headers: HeaderMap,
50}
51
52impl<'de> Deserialize<'de> for RemoteRelease {
53 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
54 where
55 D: Deserializer<'de>,
56 {
57 #[derive(Deserialize)]
58 struct InnerRemoteRelease {
59 #[serde(alias = "name")]
60 version: Version,
61 notes: Option<String>,
62 pub_date: Option<String>,
63 platforms: Option<HashMap<String, ReleaseManifestPlatform>>,
64 url: Option<Url>,
65 signature: Option<String>,
66 }
67
68 let release = InnerRemoteRelease::deserialize(deserializer)?;
69 let pub_date = match release.pub_date {
70 Some(date) => Some(
71 OffsetDateTime::parse(&date, &time::format_description::well_known::Rfc3339)
72 .map_err(|error| {
73 DeError::custom(format!("invalid value for `pub_date`: {error}"))
74 })?,
75 ),
76 None => None,
77 };
78
79 let data = match release.platforms {
80 Some(platforms) => RemoteReleaseInner::Static { platforms },
81 None => RemoteReleaseInner::Dynamic(ReleaseManifestPlatform {
82 url: release.url.ok_or_else(|| {
83 DeError::custom("the `url` field was not set on the updater response")
84 })?,
85 signature: release.signature.ok_or_else(|| {
86 DeError::custom("the `signature` field was not set on the updater response")
87 })?,
88 }),
89 };
90
91 Ok(Self {
92 version: release.version,
93 notes: release.notes,
94 pub_date,
95 data,
96 download_headers: HeaderMap::new(),
97 })
98 }
99}
100
101impl RemoteRelease {
102 pub fn download_url(&self, target: &str) -> crate::Result<&Url> {
107 match &self.data {
108 RemoteReleaseInner::Dynamic(platform) => Ok(&platform.url),
109 RemoteReleaseInner::Static { platforms } => platforms
110 .get(target)
111 .map(|platform| &platform.url)
112 .ok_or_else(|| crate::Error::TargetNotFound(target.into())),
113 }
114 }
115
116 pub fn signature(&self, target: &str) -> crate::Result<&String> {
118 match &self.data {
119 RemoteReleaseInner::Dynamic(platform) => Ok(&platform.signature),
120 RemoteReleaseInner::Static { platforms } => platforms
121 .get(target)
122 .map(|platform| &platform.signature)
123 .ok_or_else(|| crate::Error::TargetNotFound(target.into())),
124 }
125 }
126}
127
128#[derive(Debug, Clone)]
133pub struct Update {
134 pub current_version: Version,
136 pub version: Version,
138 pub date: Option<OffsetDateTime>,
140 pub body: Option<String>,
142 pub raw_json: serde_json::Value,
144 pub download_url: Url,
146 pub signature: String,
148 pub pubkey: String,
150 pub target: String,
152 pub installer_kind: InstallerKind,
154 pub headers: HeaderMap,
156 pub timeout: Option<Duration>,
158 pub proxy: Option<Url>,
160 pub no_proxy: bool,
162 pub dangerous_accept_invalid_certs: bool,
164 pub dangerous_accept_invalid_hostnames: bool,
166 pub extract_path: PathBuf,
168 pub app_name: String,
170 pub installer_args: Vec<OsString>,
172}