file manager
git clone https://git.lucas.co/cce-files.git
Support custom MIME associations via ~/.config/cce/mime.kdl
Cargo.toml | 2 ++
src/main.rs | 8 ++----
src/services/fs.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 79 insertions(+), 7 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 54da47b..570f53d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,8 @@ tokio = { version = "1", features = ["full"] }
notify = "8.2.0"
image = { version = "0.25.10", features = ["png", "jpeg"] }
log = "0.4"
+kdl = "6.7.1"
+
[lib]
diff --git a/src/main.rs b/src/main.rs
index 8fec0a3..85ca716 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -715,9 +715,7 @@ impl Application for FilesystemApp {
println!("{}", path.display());
std::process::exit(0);
} else {
- let _ = std::process::Command::new("xdg-open")
- .arg(&path)
- .spawn();
+ crate::services::fs::open_file(&path);
}
}
} else {
@@ -734,9 +732,7 @@ impl Application for FilesystemApp {
println!("{}", path.display());
std::process::exit(0);
} else {
- let _ = std::process::Command::new("xdg-open")
- .arg(&path)
- .spawn();
+ crate::services::fs::open_file(&path);
}
}
}
diff --git a/src/services/fs.rs b/src/services/fs.rs
index 3fc240b..b4e7b97 100644
--- a/src/services/fs.rs
+++ b/src/services/fs.rs
@@ -401,8 +401,54 @@ pub fn get_mime_type(path: &Path) -> Option<String> {
None
}
+pub fn load_kdl_associations() -> Option<std::collections::HashMap<String, String>> {
+ let home = std::env::var("HOME").ok()?;
+ let path = PathBuf::from(home).join(".config").join("cce").join("mime.kdl");
+ if !path.exists() {
+ return None;
+ }
+ let content = fs::read_to_string(path).ok()?;
+ let doc: kdl::KdlDocument = content.parse().ok()?;
+ let mut map = std::collections::HashMap::new();
+
+ if let Some(associations_node) = doc.get("associations") {
+ for child in associations_node.iter_children() {
+ if child.name().value() == "association" {
+ let mime = child.get(0)
+ .or_else(|| child.get("mime"))
+ .and_then(|val| match val {
+ kdl::KdlValue::String(s) => Some(s.clone()),
+ _ => None,
+ });
+ let exec = child.get(1)
+ .or_else(|| child.get("exec"))
+ .and_then(|val| match val {
+ kdl::KdlValue::String(s) => Some(s.clone()),
+ _ => None,
+ });
+ if let (Some(m), Some(e)) = (mime, exec) {
+ map.insert(m, e);
+ }
+ }
+ }
+ }
+
+ if map.is_empty() {
+ None
+ } else {
+ Some(map)
+ }
+}
+
pub fn get_default_application(mime: &str) -> Option<(String, String)> {
- // Query default handler desktop file name
+ // 1. Check KDL configuration file override
+ if let Some(associations) = load_kdl_associations() {
+ if let Some(custom_exec) = associations.get(mime) {
+ return Some((custom_exec.clone(), custom_exec.clone()));
+ }
+ }
+
+ // 2. Query default handler desktop file name
let output = std::process::Command::new("xdg-mime")
.args(&["query", "default", mime])
.output()
@@ -463,3 +509,31 @@ pub fn get_default_application(mime: &str) -> Option<(String, String)> {
}
}
+pub fn open_file(path: &Path) {
+ let mut opened = false;
+ if let Some(mime) = get_mime_type(path) {
+ if let Some((_, cmd)) = get_default_application(&mime) {
+ if !cmd.is_empty() {
+ let parts: Vec<&str> = cmd.split_whitespace().collect();
+ if !parts.is_empty() {
+ let program = parts[0];
+ let mut command = std::process::Command::new(program);
+ for arg in &parts[1..] {
+ command.arg(arg);
+ }
+ command.arg(path);
+ if command.spawn().is_ok() {
+ opened = true;
+ }
+ }
+ }
+ }
+ }
+ if !opened {
+ let _ = std::process::Command::new("xdg-open")
+ .arg(path)
+ .spawn();
+ }
+}
+
+