wslpath2/
lib.rs

1//! Convert Windows paths to WSL paths and vice versa
2//!
3//! # Example
4//!
5//! ```
6//! use wslpath2::{convert, Conversion};
7//!
8//! let path = convert("/mnt/c", None, Conversion::WslToWindows, false).unwrap_or_default();
9//!
10//! assert_eq!(path, "C:\\");
11//! ```
12
13#![warn(missing_docs)]
14
15use std::process::Command;
16
17#[cfg(windows)]
18use std::os::windows::process::CommandExt;
19
20/// Type of conversion to perform
21#[derive(Debug)]
22pub enum Conversion {
23    /// Convert Windows path to WSL path
24    WindowsToWsl,
25    /// Convert WSL path to Windows path
26    WslToWindows,
27    /// Convert WSL path to Windows path using Linux style path separators
28    WslToWindowsLinuxStyle,
29}
30
31/// Convert Paths using the `wslpath`
32///
33/// # Arguments
34///
35/// * `path` - The path to convert
36/// * `distro` - The distro to use for conversion (when calling from Windows) [optional]
37/// * `options` - The type of conversion to perform
38/// * `force_absolute_path` - Force the path to be absolute
39///
40/// # Example
41///
42/// ```
43/// use wslpath2::{convert, Conversion};
44///
45/// let path = convert("/mnt/c", None, Conversion::WslToWindows, false).unwrap_or_default();
46///
47/// assert_eq!(path, "C:\\");
48/// ```
49pub fn convert(
50    path: &str,
51    distro: Option<&str>,
52    options: Conversion,
53    force_absolute_path: bool,
54) -> Result<String, Box<dyn std::error::Error>> {
55    let mut args = Vec::new();
56
57    if let Some(distro) = distro {
58        args.push("-d");
59        args.push(distro);
60    }
61    args.push("-e");
62    args.push("wslpath");
63
64    // Select path arg
65    // Based on this conversion takes place
66    args.push(match options {
67        Conversion::WindowsToWsl => "-u",
68        Conversion::WslToWindows => "-w",
69        Conversion::WslToWindowsLinuxStyle => "-m",
70    });
71
72    // force absolute path arg
73    if force_absolute_path {
74        args.push("-a");
75    }
76
77    let mut cmd = Command::new("wsl.exe");
78    cmd.args(args);
79    cmd.arg(path.replace('\\', "\\\\"));
80
81    // Disable window creation on Windows
82    //
83    // This is necessary to prevent a command prompt window from being shown for a short time,
84    // which is likely undesired, especially for GUI applications.
85    //
86    // The flags are documented here:
87    // https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags#flags
88    #[cfg(windows)]
89    cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW
90
91    let output = cmd
92        .output()
93        .map_err(|e| format!("Error executing wsl.exe: {}", e))?;
94
95    let code = output.status.code().unwrap_or(-1);
96    if code != 0 {
97        return Err(format!("Error getting wslpath: {}", code).into());
98    }
99
100    Ok(std::str::from_utf8(&output.stdout)
101        .map_err(|e| format!("Error converting output to string: {}", e))?
102        .trim()
103        .to_string())
104}
105
106#[cfg(test)]
107mod tests {
108    use crate::{convert, Conversion};
109
110    // These tests may not execute on all machines as they require WSL
111
112    #[test]
113    fn test_wsl_to_windows() {
114        assert_eq!(
115            convert("/mnt/c", None, Conversion::WslToWindows, false).unwrap_or_default(),
116            "C:\\"
117        );
118    }
119
120    #[test]
121    fn test_wsl_to_windows_absolute() {
122        assert_eq!(
123            convert("/mnt/c", None, Conversion::WslToWindows, true).unwrap_or_default(),
124            "C:\\"
125        );
126    }
127
128    #[test]
129    fn test_wsl_to_windows_linux_style() {
130        assert_eq!(
131            convert("/mnt/c", None, Conversion::WslToWindowsLinuxStyle, false).unwrap_or_default(),
132            "C:/"
133        );
134    }
135
136    #[test]
137    fn test_wsl_to_windows_linux_style_absolute() {
138        assert_eq!(
139            convert("/mnt/c", None, Conversion::WslToWindowsLinuxStyle, true).unwrap_or_default(),
140            "C:/"
141        );
142    }
143
144    #[test]
145    fn test_windows_to_wsl() {
146        assert_eq!(
147            convert("C:/", None, Conversion::WindowsToWsl, false).unwrap_or_default(),
148            "/mnt/c/"
149        );
150    }
151
152    #[test]
153    fn test_windows_to_wsl_absolute() {
154        assert_eq!(
155            convert("C:/", None, Conversion::WindowsToWsl, true).unwrap_or_default(),
156            "/mnt/c/"
157        );
158    }
159}