use relm4::gtk::gio;
use relm4::gtk::glib;
use crate::cmd::Command;
use crate::ipc;
pub const BUS_NAME: &str = "org.freedesktop.FileManager1";
const OBJECT_PATH: &str = "/org/freedesktop/FileManager1";
const INTERFACE_XML: &str = r#"<node>
<interface name="org.freedesktop.FileManager1">
<method name="ShowFolders">
<arg type="as" name="URIs" direction="in"/>
<arg type="s" name="StartupId" direction="in"/>
</method>
<method name="ShowItems">
<arg type="as" name="URIs" direction="in"/>
<arg type="s" name="StartupId" direction="in"/>
</method>
<method name="ShowItemProperties">
<arg type="as" name="URIs" direction="in"/>
<arg type="s" name="StartupId" direction="in"/>
</method>
</interface>
</node>"#;
pub fn serve_file_manager() {
gio::bus_own_name(
gio::BusType::Session,
BUS_NAME,
gio::BusNameOwnerFlags::empty(),
|connection, _name| register_object(&connection),
|_connection, _name| {},
|_connection, _name| {},
);
}
fn register_object(connection: &gio::DBusConnection) {
let Ok(node) = gio::DBusNodeInfo::for_xml(INTERFACE_XML) else {
return;
};
let Some(interface) = node.lookup_interface(BUS_NAME) else {
return;
};
let _ = connection
.register_object(OBJECT_PATH, &interface)
.method_call(|_conn, _sender, _path, _iface, _method, params, invocation| {
let uris: Vec<String> =
params.get::<(Vec<String>, String)>().map(|(uris, _)| uris).unwrap_or_default();
let command = command_for_uris(&uris);
if !command.reveal.is_empty() {
let _ = ipc::deliver(&command);
}
invocation.return_value(None);
})
.build();
}
fn command_for_uris(uris: &[String]) -> Command {
let mut command = Command::default();
for uri in uris {
if let Ok((path, _hostname)) = glib::filename_from_uri(uri) {
command.reveal.push(path);
}
}
command
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn file_uris_become_decoded_reveals() {
let uris = vec!["file:///home/eolu/My%20Files/notes.md".to_owned()];
assert_eq!(
command_for_uris(&uris).reveal,
vec![PathBuf::from("/home/eolu/My Files/notes.md")]
);
}
#[test]
fn non_file_uris_are_ignored() {
let uris = vec!["https://example.com/".to_owned(), "trash:///x".to_owned()];
assert!(command_for_uris(&uris).reveal.is_empty());
}
#[test]
fn multiple_uris_all_reveal() {
let uris =
vec!["file:///tmp/a".to_owned(), "file:///tmp/b%20c".to_owned()];
assert_eq!(
command_for_uris(&uris).reveal,
vec![PathBuf::from("/tmp/a"), PathBuf::from("/tmp/b c")]
);
}
}