From a8b17926edd422dfb52abbe9dc7c5148eac2e1b5 Mon Sep 17 00:00:00 2001 From: Slinetrac Date: Tue, 21 Oct 2025 22:53:47 +0800 Subject: [PATCH] refactor: adjust MIME detection to merge duplicates and follow Freedesktop standard - Honor Freedesktop precedence when locating mimeapps.list - Replace per-scheme HashSet with index-tracking HashMap - Merge duplicate handler entries instead of discarding them - Ensure all schemes exist using the new tracking structure --- src-tauri/src/utils/linux.rs | 106 +++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 31 deletions(-) diff --git a/src-tauri/src/utils/linux.rs b/src-tauri/src/utils/linux.rs index 67800859..fd85338b 100644 --- a/src-tauri/src/utils/linux.rs +++ b/src-tauri/src/utils/linux.rs @@ -1,7 +1,7 @@ use crate::logging; use crate::utils::logging::Type; use anyhow::Result; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::env; use std::fs; use std::path::PathBuf; @@ -559,6 +559,25 @@ pub fn ensure_mimeapps_entries(desktop_file: &str, schemes: &[&str]) -> Result<( } fn mimeapps_list_path() -> Option { + let config_path = env::var_os("XDG_CONFIG_HOME") + .map(PathBuf::from) + .or_else(|| { + env::var_os("HOME").map(PathBuf::from).map(|mut home| { + home.push(".config"); + home + }) + }) + .map(|mut dir| { + dir.push("mimeapps.list"); + dir + }); + + if let Some(ref path) = config_path { + if path.exists() { + return Some(path.clone()); + } + } + let data_path = env::var_os("XDG_DATA_HOME") .map(PathBuf::from) .or_else(|| { @@ -580,25 +599,6 @@ fn mimeapps_list_path() -> Option { } } - let config_path = env::var_os("XDG_CONFIG_HOME") - .map(PathBuf::from) - .or_else(|| { - env::var_os("HOME").map(PathBuf::from).map(|mut home| { - home.push(".config"); - home - }) - }) - .map(|mut dir| { - dir.push("mimeapps.list"); - dir - }); - - if let Some(ref path) = config_path { - if path.exists() { - return Some(path.clone()); - } - } - config_path } @@ -616,7 +616,7 @@ fn flush_section( kind: SectionKind, changed: &mut bool, ) { - let mut seen: HashSet<&str> = HashSet::new(); + let mut seen: HashMap<&str, usize> = HashMap::new(); let mut processed: Vec = Vec::with_capacity(section.len()); for line in section.drain(..) { @@ -632,15 +632,6 @@ fn flush_section( }; if let Some(scheme) = match_scheme(raw_key.trim(), schemes) { - if !seen.insert(scheme) { - *changed = true; - continue; - } - - let prefix = line - .chars() - .take_while(|c| c.is_whitespace()) - .collect::(); let mut values: Vec = raw_value .split(';') .filter_map(|value| { @@ -649,6 +640,53 @@ fn flush_section( }) .collect(); + if let Some(&index) = seen.get(scheme) { + let existing_line = &mut processed[index]; + let existing_prefix: String = existing_line + .chars() + .take_while(|c| c.is_whitespace()) + .collect(); + let Some((_, existing_raw_value)) = existing_line.trim().split_once('=') else { + processed.push(line); + continue; + }; + + let mut merged_values: Vec = existing_raw_value + .split(';') + .filter_map(|value| { + let trimmed = value.trim(); + (!trimmed.is_empty()).then(|| trimmed.to_string()) + }) + .collect(); + + for value in values { + if !merged_values.iter().any(|existing| existing == &value) { + merged_values.push(value); + } + } + + if let Some(pos) = merged_values.iter().position(|value| value == desktop_file) { + if pos != 0 { + let moved = merged_values.remove(pos); + merged_values.insert(0, moved); + } + } else { + merged_values.insert(0, desktop_file.to_string()); + } + + let mut merged_line = format!("{existing_prefix}x-scheme-handler/{scheme}="); + merged_line.push_str(&merged_values.join(";")); + merged_line.push(';'); + + if *existing_line != merged_line { + *existing_line = merged_line; + } + + // Dropping the duplicate entry alters the section even if nothing new was added. + *changed = true; + continue; + } + if let Some(pos) = values.iter().position(|value| value == desktop_file) { if pos != 0 { values.remove(pos); @@ -660,6 +698,10 @@ fn flush_section( *changed = true; } + let prefix = line + .chars() + .take_while(|c| c.is_whitespace()) + .collect::(); let mut new_line = format!("{prefix}x-scheme-handler/{scheme}="); new_line.push_str(&values.join(";")); new_line.push(';'); @@ -668,7 +710,9 @@ fn flush_section( *changed = true; } + let index = processed.len(); processed.push(new_line); + seen.insert(scheme, index); continue; } @@ -682,7 +726,7 @@ fn flush_section( if ensure_all { for &scheme in schemes { - if !seen.contains(scheme) { + if !seen.contains_key(scheme) { processed.push(format!("x-scheme-handler/{scheme}={desktop_file};")); *changed = true; }