use std::path::PathBuf;
pub fn find_best() -> Option<PathBuf> {
if let Ok(path) = std::env::var("NVIM") {
let p = PathBuf::from(&path);
if is_socket(&p) {
return Some(p);
}
}
let runtime = get_runtime_dir();
if let Some(s) = best_socket_in(&runtime) {
return Some(s);
}
best_socket_in("/tmp")
}
pub fn find_all() -> Vec<PathBuf> {
let mut result = Vec::new();
let runtime = get_runtime_dir();
collect_sockets(&runtime, &mut result);
collect_sockets("/tmp", &mut result);
sort_by_mtime(&mut result);
result
}
fn get_runtime_dir() -> String {
std::env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| {
let output = std::process::Command::new("id")
.arg("-u")
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.unwrap_or_else(|| "1000".to_string());
format!("/run/user/{}", output.trim())
})
}
fn best_socket_in(dir: &str) -> Option<PathBuf> {
let mut sockets = Vec::new();
collect_sockets(dir, &mut sockets);
sort_by_mtime(&mut sockets);
sockets.into_iter().last()
}
fn collect_sockets(dir: &str, out: &mut Vec<PathBuf>) {
let entries = match std::fs::read_dir(dir) {
Ok(e) => e,
Err(_) => return,
};
for entry in entries.filter_map(|e| e.ok()) {
let name = entry.file_name();
if !name.to_string_lossy().starts_with("nvim") {
continue;
}
let path = entry.path();
if is_socket(&path) {
out.push(path);
} else if path.is_dir() {
if let Ok(sub) = std::fs::read_dir(&path) {
for s in sub.filter_map(|x| x.ok()) {
let sp = s.path();
if is_socket(&sp) {
out.push(sp);
}
}
}
}
}
}
fn is_socket(p: &std::path::Path) -> bool {
#[cfg(unix)]
{
use std::os::unix::fs::FileTypeExt;
p.metadata()
.map(|m| m.file_type().is_socket())
.unwrap_or(false)
}
#[cfg(not(unix))]
{
p.exists()
}
}
fn sort_by_mtime(paths: &mut Vec<PathBuf>) {
paths.sort_by_key(|p| p.metadata().and_then(|m| m.modified()).ok());
}