Skip to content

Commit caf1ba6

Browse files
committed
fix(integrate): don't clobber desktop Icon field when package ships no matching icon
1 parent 7e0f7ca commit caf1ba6

2 files changed

Lines changed: 37 additions & 15 deletions

File tree

crates/soar-package/src/formats/appimage.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ pub async fn integrate_appimage<P: AsRef<Path>, T: PackageExt>(
4646
let pkg_name = package.pkg_name();
4747
let mut appimage = AppImage::new(None, &file_path, None)?;
4848

49+
// Track whether an icon ends up available for the extracted desktop file.
50+
// Both the extracted icon and desktop file are named after `pkg_name`, so
51+
// their stems match.
52+
let mut icon_available = has_icon;
4953
if !has_icon {
5054
if let Some(entry) = appimage.find_icon() {
5155
if entry.kind == AppImageEntryKind::File {
@@ -63,6 +67,7 @@ pub async fn integrate_appimage<P: AsRef<Path>, T: PackageExt>(
6367
.with_context(|| format!("renaming from {dest} to {final_path}"))?;
6468

6569
symlink_icon_with_mode(final_path, config.is_system())?;
70+
icon_available = true;
6671
}
6772
}
6873
}
@@ -72,7 +77,7 @@ pub async fn integrate_appimage<P: AsRef<Path>, T: PackageExt>(
7277
if entry.kind == AppImageEntryKind::File {
7378
let dest = format!("{}/{}.desktop", install_dir.display(), pkg_name);
7479
let _ = appimage.write_entry(&entry, &dest);
75-
symlink_desktop_with_config(dest, package, config)?;
80+
symlink_desktop_with_config(dest, package, icon_available, config)?;
7681
}
7782
}
7883
}

crates/soar-package/src/formats/common.rs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! icon handling, desktop file creation, and portable directory setup.
55
66
use std::{
7+
collections::HashSet,
78
env,
89
ffi::OsStr,
910
fs::{self, File},
@@ -133,14 +134,17 @@ pub fn symlink_icon_with_mode<P: AsRef<Path>>(real_path: P, system_mode: bool) -
133134

134135
/// Creates a symlink for a desktop file with modified fields.
135136
///
136-
/// Updates the Icon, Exec, and TryExec fields in the desktop file to point
137-
/// to the installed package, then creates a symlink in the applications
138-
/// directory.
137+
/// Updates the Exec and TryExec fields in the desktop file to point to the
138+
/// installed package, then creates a symlink in the applications directory.
139+
/// The Icon field is rewritten to the soar-managed icon only when the package
140+
/// ships a matching icon (`has_icon`); otherwise it is left untouched so that
141+
/// references to generic system icons keep working.
139142
///
140143
/// # Arguments
141144
///
142145
/// * `real_path` - Path to the desktop file
143146
/// * `package` - Package metadata
147+
/// * `has_icon` - Whether a matching soar-managed icon exists for this desktop file
144148
///
145149
/// # Returns
146150
///
@@ -152,8 +156,9 @@ pub fn symlink_icon_with_mode<P: AsRef<Path>>(real_path: P, system_mode: bool) -
152156
pub fn symlink_desktop<P: AsRef<Path>, T: PackageExt>(
153157
real_path: P,
154158
package: &T,
159+
has_icon: bool,
155160
) -> Result<PathBuf> {
156-
symlink_desktop_with_config(real_path, package, &get_config())
161+
symlink_desktop_with_config(real_path, package, has_icon, &get_config())
157162
}
158163

159164
/// Creates a symlink for a desktop file using the provided config.
@@ -163,6 +168,7 @@ pub fn symlink_desktop<P: AsRef<Path>, T: PackageExt>(
163168
pub fn symlink_desktop_with_config<P: AsRef<Path>, T: PackageExt>(
164169
real_path: P,
165170
package: &T,
171+
has_icon: bool,
166172
config: &soar_config::config::Config,
167173
) -> Result<PathBuf> {
168174
let pkg_name = package.pkg_name();
@@ -179,7 +185,8 @@ pub fn symlink_desktop_with_config<P: AsRef<Path>, T: PackageExt>(
179185

180186
re.replace_all(&content, |caps: &regex::Captures| {
181187
match &caps[1] {
182-
"Icon" => format!("Icon={}-soar", file_name.to_string_lossy()),
188+
"Icon" if has_icon => format!("Icon={}-soar", file_name.to_string_lossy()),
189+
"Icon" => caps[0].to_string(),
183190
"Exec" | "TryExec" => {
184191
let old_cmd = &caps[2];
185192
let parts: Vec<&str> = old_cmd.split_whitespace().collect();
@@ -375,31 +382,41 @@ pub async fn integrate_package<P: AsRef<Path>, T: PackageExt>(
375382

376383
let system_mode = config.is_system();
377384

378-
let mut has_desktop = false;
379385
let mut has_icon = false;
386+
let mut icon_stems: HashSet<String> = HashSet::new();
380387
let mut symlink_action = |path: &Path| -> Result<()> {
381-
// Never treat the package binary itself as a desktop file. Its name can
382-
// legitimately end in `.desktop`, but its contents are the executable.
383388
if path == bin_path.as_path() {
384389
return Ok(());
385390
}
386391
let ext = path.extension();
387-
if ext == Some(OsStr::new("desktop")) {
388-
has_desktop = true;
389-
symlink_desktop_with_config(path, package, config)?;
392+
if ext == Some(OsStr::new("png")) || ext == Some(OsStr::new("svg")) {
393+
has_icon = true;
394+
if let Some(stem) = path.file_stem() {
395+
icon_stems.insert(stem.to_string_lossy().into_owned());
396+
}
397+
symlink_icon_with_mode(path, system_mode)?;
390398
}
391399
Ok(())
392400
};
393401
walk_dir(install_dir, &mut symlink_action)?;
394402

403+
let mut has_desktop = false;
395404
let mut symlink_action = |path: &Path| -> Result<()> {
405+
// Never treat the package binary itself as a desktop file. Its name can
406+
// legitimately end in `.desktop`, but its contents are the executable.
396407
if path == bin_path.as_path() {
397408
return Ok(());
398409
}
399410
let ext = path.extension();
400-
if ext == Some(OsStr::new("png")) || ext == Some(OsStr::new("svg")) {
401-
has_icon = true;
402-
symlink_icon_with_mode(path, system_mode)?;
411+
if ext == Some(OsStr::new("desktop")) {
412+
has_desktop = true;
413+
// Only rewrite the Icon field when this desktop file has a matching
414+
// icon shipped by the package (matched by file stem).
415+
let desktop_has_icon = path
416+
.file_stem()
417+
.map(|stem| icon_stems.contains(&*stem.to_string_lossy()))
418+
.unwrap_or(false);
419+
symlink_desktop_with_config(path, package, desktop_has_icon, config)?;
403420
}
404421
Ok(())
405422
};

0 commit comments

Comments
 (0)