From 4210cf316a8f910c8563f7fb76bc2de56156ae38 Mon Sep 17 00:00:00 2001 From: matthme <36768177+matthme@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:54:09 +0000 Subject: [PATCH] feat(single-instance): Add semver compatibility check for Windows and Linux (#502) * added semver compatibility check for Windows and Linux * fixed formatting * put semver feature behind a feature flag * remove semver from root manifest * linux compile error * docs: Add mention for semver feature in readme --------- Co-authored-by: FabianLars --- Cargo.lock | 1 + plugins/log/README.md | 4 ++- plugins/single-instance/Cargo.toml | 4 +++ plugins/single-instance/src/lib.rs | 3 +++ .../src/platform_impl/linux.rs | 26 ++++++++++++++++++- .../src/platform_impl/windows.rs | 11 +++++++- plugins/single-instance/src/semver_compat.rs | 17 ++++++++++++ 7 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 plugins/single-instance/src/semver_compat.rs diff --git a/Cargo.lock b/Cargo.lock index 3d65657d..7b94d50c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4907,6 +4907,7 @@ name = "tauri-plugin-single-instance" version = "0.0.0" dependencies = [ "log", + "semver", "serde", "serde_json", "tauri", diff --git a/plugins/log/README.md b/plugins/log/README.md index 2d2e8534..613103d3 100644 --- a/plugins/log/README.md +++ b/plugins/log/README.md @@ -21,7 +21,9 @@ Install the Core plugin by adding the following to your `Cargo.toml` file: tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } ``` -You can install the JavaScript Guest bindings using your preferred JavaScript package manager: +If you want the single instance mechanism to only trigger for semver compatible instances of your apps, for example if you expect users to have multiple installations of your app installed, you can add `features = ["semver"]` to the dependency declaration in `Cargo.toml`. + +Then you can install the JavaScript Guest bindings using your preferred JavaScript package manager: > Note: Since most JavaScript package managers are unable to install packages from git monorepos we provide read-only mirrors of each plugin. This makes installation option 2 more ergonomic to use. diff --git a/plugins/single-instance/Cargo.toml b/plugins/single-instance/Cargo.toml index b0d703ba..f22c476e 100644 --- a/plugins/single-instance/Cargo.toml +++ b/plugins/single-instance/Cargo.toml @@ -16,6 +16,7 @@ serde_json = { workspace = true } tauri = { workspace = true } log = { workspace = true } thiserror = { workspace = true } +semver = { version = "1", optional = true } [target.'cfg(target_os = "windows")'.dependencies.windows-sys] version = "0.52" @@ -31,3 +32,6 @@ features = [ [target.'cfg(target_os = "linux")'.dependencies] zbus = "3" + +[features] +semver = ["dep:semver"] \ No newline at end of file diff --git a/plugins/single-instance/src/lib.rs b/plugins/single-instance/src/lib.rs index a2ff71af..365c77b4 100644 --- a/plugins/single-instance/src/lib.rs +++ b/plugins/single-instance/src/lib.rs @@ -10,6 +10,9 @@ mod platform_impl; #[path = "platform_impl/macos.rs"] mod platform_impl; +#[cfg(feature = "semver")] +mod semver_compat; + pub(crate) type SingleInstanceCallback = dyn FnMut(&AppHandle, Vec, String) + Send + Sync + 'static; diff --git a/plugins/single-instance/src/platform_impl/linux.rs b/plugins/single-instance/src/platform_impl/linux.rs index 513526d4..2f4747db 100644 --- a/plugins/single-instance/src/platform_impl/linux.rs +++ b/plugins/single-instance/src/platform_impl/linux.rs @@ -2,6 +2,9 @@ use std::sync::Arc; +#[cfg(feature = "semver")] +use crate::semver_compat::semver_compat_string; + use crate::SingleInstanceCallback; use tauri::{ plugin::{self, TauriPlugin}, @@ -26,6 +29,15 @@ impl SingleInstanceDBus { } } +#[cfg(feature = "semver")] +fn dbus_id(config: Arc, version: semver::Version) -> String { + let mut id = config.tauri.bundle.identifier.replace(['.', '-'], "_"); + id.push('_'); + id.push_str(semver_compat_string(version).as_str()); + id +} + +#[cfg(not(feature = "semver"))] fn dbus_id(config: Arc) -> String { config.tauri.bundle.identifier.replace(['.', '-'], "_") } @@ -33,7 +45,11 @@ fn dbus_id(config: Arc) -> String { pub fn init(f: Box>) -> TauriPlugin { plugin::Builder::new("single-instance") .setup(|app| { + #[cfg(feature = "semver")] + let id = dbus_id(app.config(), app.package_info().version.clone()); + #[cfg(not(feature = "semver"))] let id = dbus_id(app.config()); + let single_instance_dbus = SingleInstanceDBus { callback: f, app_handle: app.clone(), @@ -85,7 +101,15 @@ pub fn init(f: Box>) -> TauriPlugin { pub fn destroy>(manager: &M) { if let Some(connection) = manager.try_state::() { - let dbus_name = format!("org.{}.SingleInstance", dbus_id(manager.config())); + #[cfg(feature = "semver")] + let id = dbus_id( + manager.config(), + manager.app_handle().package_info().version.clone(), + ); + #[cfg(not(feature = "semver"))] + let id = dbus_id(manager.config()); + + let dbus_name = format!("org.{id}.SingleInstance",); let _ = connection.0.release_name(dbus_name); } } diff --git a/plugins/single-instance/src/platform_impl/windows.rs b/plugins/single-instance/src/platform_impl/windows.rs index 63e95645..bfdc0067 100644 --- a/plugins/single-instance/src/platform_impl/windows.rs +++ b/plugins/single-instance/src/platform_impl/windows.rs @@ -1,5 +1,8 @@ #![cfg(target_os = "windows")] +#[cfg(feature = "semver")] +use crate::semver_compat::semver_compat_string; + use crate::SingleInstanceCallback; use std::ffi::CStr; use tauri::{ @@ -29,7 +32,13 @@ const WMCOPYDATA_SINGLE_INSTANCE_DATA: usize = 1542; pub fn init(f: Box>) -> TauriPlugin { plugin::Builder::new("single-instance") .setup(|app| { - let id = &app.config().tauri.bundle.identifier; + #[allow(unused_mut)] + let mut id = app.config().tauri.bundle.identifier.clone(); + #[cfg(feature = "semver")] + { + id.push('_'); + id.push_str(semver_compat_string(app.package_info().version.clone()).as_str()); + } let class_name = encode_wide(format!("{id}-sic")); let window_name = encode_wide(format!("{id}-siw")); diff --git a/plugins/single-instance/src/semver_compat.rs b/plugins/single-instance/src/semver_compat.rs new file mode 100644 index 00000000..287f52be --- /dev/null +++ b/plugins/single-instance/src/semver_compat.rs @@ -0,0 +1,17 @@ +#![cfg(feature = "semver")] + +/// Takes a version and spits out a String with trailing _x, thus only considering the digits +/// relevant regarding semver compatibility +pub fn semver_compat_string(version: semver::Version) -> String { + // for pre-release always treat each version separately + if !version.pre.is_empty() { + return version.to_string().replace(['.', '-'], "_"); + } + match version.major { + 0 => match version.minor { + 0 => format!("0_0_{}", version.patch), + _ => format!("0_{}_x", version.minor), + }, + _ => format!("{}_x_x", version.major), + } +}