open/
unix.rs

1use std::{
2    env,
3    ffi::{OsStr, OsString},
4    io,
5    path::{Path, PathBuf},
6    process::Command,
7};
8
9use crate::{CommandExt, IntoResult};
10
11pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
12    let path = path.as_ref();
13    let open_handlers = [
14        ("xdg-open", &[path] as &[_]),
15        ("gio", &[OsStr::new("open"), path]),
16        ("gnome-open", &[path]),
17        ("kde-open", &[path]),
18        ("wslview", &[&wsl_path(path)]),
19    ];
20
21    let mut unsuccessful = None;
22    let mut io_error = None;
23
24    for (command, args) in &open_handlers {
25        let result = Command::new(command).args(*args).status_without_output();
26
27        match result {
28            Ok(status) if status.success() => return Ok(()),
29            Ok(status) => {
30                unsuccessful = unsuccessful.or_else(|| {
31                    Some(std::io::Error::new(
32                        std::io::ErrorKind::Other,
33                        status.to_string(),
34                    ))
35                })
36            }
37            Err(err) => io_error = io_error.or(Some(err)),
38        }
39    }
40
41    Err(unsuccessful
42        .or(io_error)
43        .expect("successful cases don't get here"))
44}
45
46pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
47    Command::new(app.into())
48        .arg(path.as_ref())
49        .status_without_output()
50        .into_result()
51}
52
53// Polyfill to workaround absolute path bug in wslu(wslview). In versions before
54// v3.1.1, wslview is unable to find absolute paths. `wsl_path` converts an
55// absolute path into a relative path starting from the current directory. If
56// the path is already a relative path or the conversion fails the original path
57// is returned.
58fn wsl_path<T: AsRef<OsStr>>(path: T) -> OsString {
59    fn path_relative_to_current_dir<T: AsRef<OsStr>>(path: T) -> Option<PathBuf> {
60        let path = Path::new(&path);
61
62        if path.is_relative() {
63            return None;
64        }
65
66        let base = env::current_dir().ok()?;
67        pathdiff::diff_paths(path, base)
68    }
69
70    match path_relative_to_current_dir(&path) {
71        None => OsString::from(&path),
72        Some(relative) => OsString::from(relative),
73    }
74}