diff --git a/.changes/update-available-refactor.md b/.changes/update-available-refactor.md new file mode 100644 index 000000000..d4ed04d33 --- /dev/null +++ b/.changes/update-available-refactor.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +**Breaking change:** The `tauri::UpdaterEvent::UpdateEvent` date field is now an `Option`. diff --git a/.changes/update-date-strong-type.md b/.changes/update-date-strong-type.md new file mode 100644 index 000000000..3a134aaaf --- /dev/null +++ b/.changes/update-date-strong-type.md @@ -0,0 +1,5 @@ +--- +"tauri": patch +--- + +**Breaking change:** The updater response `pub_date` now must be a valid RFC 3339 string. diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 960f396a2..e2e6423d1 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -84,6 +84,7 @@ os_pipe = { version = "1.0", optional = true } rfd = { version = "0.8", optional = true } raw-window-handle = "0.4.2" minisign-verify = { version = "0.2", optional = true } +time = { version = "0.3", features = ["parsing", "formatting"], optional = true } os_info = { version = "3.2.0", optional = true } futures-lite = "1.12" regex = { version = "1.5.5", optional = true } @@ -138,6 +139,7 @@ isolation = [ "tauri-utils/isolation", "tauri-macros/isolation" ] custom-protocol = [ "tauri-macros/custom-protocol" ] updater = [ "minisign-verify", + "time", "base64", "http-api", "dialog-ask", diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 049cabc70..386cd9d68 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -265,7 +265,7 @@ pub enum UpdaterEvent { /// The update body. body: String, /// The update release date. - date: String, + date: Option, /// The update version. version: String, }, diff --git a/core/tauri/src/updater/core.rs b/core/tauri/src/updater/core.rs index 6566812b5..f422f5b0f 100644 --- a/core/tauri/src/updater/core.rs +++ b/core/tauri/src/updater/core.rs @@ -18,6 +18,7 @@ use minisign_verify::{PublicKey, Signature}; use semver::Version; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize}; use tauri_utils::{platform::current_exe, Env}; +use time::OffsetDateTime; use url::Url; #[cfg(feature = "updater")] @@ -57,16 +58,15 @@ pub enum RemoteReleaseInner { /// Information about a release returned by the remote update server. /// /// This type can have one of two shapes: Server Format (Dynamic Format) and Static Format. -#[derive(Debug, Serialize)] +#[derive(Debug)] pub struct RemoteRelease { /// Version to install. version: Version, /// Release notes. notes: Option, /// Release date. - pub_date: String, + pub_date: Option, /// Release data. - #[serde(flatten)] data: RemoteReleaseInner, } @@ -75,17 +75,12 @@ impl<'de> Deserialize<'de> for RemoteRelease { where D: Deserializer<'de>, { - fn default_pub_date() -> String { - "N/A".to_string() - } - #[derive(Deserialize)] struct InnerRemoteRelease { #[serde(alias = "name", deserialize_with = "parse_version")] version: Version, notes: Option, - #[serde(default = "default_pub_date")] - pub_date: String, + pub_date: Option, platforms: Option>, // dynamic platform response url: Option, @@ -97,10 +92,19 @@ impl<'de> Deserialize<'de> for RemoteRelease { let release = InnerRemoteRelease::deserialize(deserializer)?; + let pub_date = if let Some(date) = release.pub_date { + Some( + OffsetDateTime::parse(&date, &time::format_description::well_known::Rfc3339) + .map_err(|e| DeError::custom(format!("invalid value for `pub_date`: {}", e)))?, + ) + } else { + None + }; + Ok(RemoteRelease { version: release.version, notes: release.notes, - pub_date: release.pub_date, + pub_date, data: if let Some(platforms) = release.platforms { RemoteReleaseInner::Static { platforms } } else { @@ -152,8 +156,8 @@ impl RemoteRelease { } /// The release date. - pub fn pub_date(&self) -> &String { - &self.pub_date + pub fn pub_date(&self) -> Option<&OffsetDateTime> { + self.pub_date.as_ref() } /// The release's download URL for the given target. @@ -431,7 +435,7 @@ impl UpdateBuilder { extract_path, should_update, version: final_release.version().to_string(), - date: final_release.pub_date().to_string(), + date: final_release.pub_date().cloned(), current_version: self.current_version, download_url: final_release.download_url(&json_target)?.to_owned(), body: final_release.notes().cloned(), @@ -461,7 +465,7 @@ pub struct Update { /// Running version pub current_version: Version, /// Update publish date - pub date: String, + pub date: Option, /// Target #[allow(dead_code)] target: String, @@ -489,7 +493,7 @@ impl Clone for Update { should_update: self.should_update, version: self.version.clone(), current_version: self.current_version.clone(), - date: self.date.clone(), + date: self.date, target: self.target.clone(), extract_path: self.extract_path.clone(), download_url: self.download_url.clone(), diff --git a/core/tauri/src/updater/mod.rs b/core/tauri/src/updater/mod.rs index 54ac14633..12d81db0b 100644 --- a/core/tauri/src/updater/mod.rs +++ b/core/tauri/src/updater/mod.rs @@ -136,7 +136,7 @@ //! tauri::RunEvent::Updater(updater_event) => { //! match updater_event { //! tauri::UpdaterEvent::UpdateAvailable { body, date, version } => { -//! println!("update available {} {} {}", body, date, version); +//! println!("update available {} {:?} {}", body, date, version); //! } //! _ => (), //! } @@ -246,7 +246,7 @@ //! tauri::RunEvent::Updater(updater_event) => { //! match updater_event { //! tauri::UpdaterEvent::UpdateAvailable { body, date, version } => { -//! println!("update available {} {} {}", body, date, version); +//! println!("update available {} {:?} {}", body, date, version); //! } //! tauri::UpdaterEvent::Pending => { //! println!("update is pending!"); @@ -450,6 +450,7 @@ use std::time::Duration; use http::header::{HeaderName, HeaderValue}; use semver::Version; +use time::OffsetDateTime; pub use self::{core::RemoteRelease, error::Error}; /// Alias for [`std::result::Result`] using our own [`Error`]. @@ -509,7 +510,7 @@ struct DownloadProgressEvent { #[derive(Clone, serde::Serialize)] struct UpdateManifest { version: String, - date: String, + date: Option, body: String, } @@ -686,14 +687,14 @@ impl UpdateBuilder { EVENT_UPDATE_AVAILABLE, UpdateManifest { body: body.clone(), - date: update.date.clone(), + date: update.date.map(|d| d.to_string()), version: update.version.clone(), }, ); let _ = handle.create_proxy().send_event(EventLoopMessage::Updater( UpdaterEvent::UpdateAvailable { body, - date: update.date.clone(), + date: update.date, version: update.version.clone(), }, )); @@ -751,8 +752,8 @@ impl UpdateResponse { } /// The update date. - pub fn date(&self) -> &str { - &self.update.date + pub fn date(&self) -> Option<&OffsetDateTime> { + self.update.date.as_ref() } /// The update description. diff --git a/core/tests/app-updater/Cargo.toml b/core/tests/app-updater/Cargo.toml index dfd512e94..641afabe8 100644 --- a/core/tests/app-updater/Cargo.toml +++ b/core/tests/app-updater/Cargo.toml @@ -11,6 +11,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" tiny_http = "0.11" tauri = { path = "../../tauri", features = ["updater"] } +time = { version = "0.3", features = ["formatting"] } [features] default = ["custom-protocol"] diff --git a/core/tests/app-updater/tests/update.rs b/core/tests/app-updater/tests/update.rs index c4755cd09..3b47b459a 100644 --- a/core/tests/app-updater/tests/update.rs +++ b/core/tests/app-updater/tests/update.rs @@ -35,6 +35,7 @@ struct PlatformUpdate { #[derive(Serialize)] struct Update { version: &'static str, + date: String, platforms: HashMap, } @@ -178,6 +179,9 @@ fn update_app() { ); let body = serde_json::to_vec(&Update { version: "1.0.0", + date: time::OffsetDateTime::now_utc() + .format(&time::format_description::well_known::Rfc3339) + .unwrap(), platforms, }) .unwrap();