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 <fabianlars@fabianlars.de>
pull/1188/head
matthme 1 year ago committed by GitHub
parent 57e979adaf
commit 4210cf316a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

1
Cargo.lock generated

@ -4907,6 +4907,7 @@ name = "tauri-plugin-single-instance"
version = "0.0.0"
dependencies = [
"log",
"semver",
"serde",
"serde_json",
"tauri",

@ -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.

@ -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"]

@ -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<R> =
dyn FnMut(&AppHandle<R>, Vec<String>, String) + Send + Sync + 'static;

@ -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<R: Runtime> SingleInstanceDBus<R> {
}
}
#[cfg(feature = "semver")]
fn dbus_id(config: Arc<Config>, 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<Config>) -> String {
config.tauri.bundle.identifier.replace(['.', '-'], "_")
}
@ -33,7 +45,11 @@ fn dbus_id(config: Arc<Config>) -> String {
pub fn init<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
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<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
pub fn destroy<R: Runtime, M: Manager<R>>(manager: &M) {
if let Some(connection) = manager.try_state::<ConnectionHandle>() {
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);
}
}

@ -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<R: Runtime>(f: Box<SingleInstanceCallback<R>>) -> TauriPlugin<R> {
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"));

@ -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),
}
}
Loading…
Cancel
Save