vscli 1.3.3

A CLI tool to launch vscode projects, which supports devcontainers.
use super::{DevContainer, Workspace, exec_code};
use crate::uri::{DevcontainerUriJson, FileUriJson};
#[cfg(unix)]
use color_eyre::eyre::eyre;
use color_eyre::eyre::{Result, WrapErr, bail};
#[cfg(unix)]
use log::debug;
use log::trace;
use std::ffi::OsString;
use std::path::Path;
#[cfg(unix)]
use std::process::Command;

impl Workspace {
    pub fn open(
        &self,
        mut args: Vec<OsString>,
        dry_run: bool,
        dev_container: &DevContainer,
        command: &str,
        subfolder: Option<&Path>,
    ) -> Result<()> {
        if args.iter().any(|arg| arg == "--folder-uri") {
            bail!("Specifying `--folder-uri` is not possible while using vscli.");
        }

        let container_folder = container_folder(dev_container, subfolder);
        let mut workspace_path = self.path.to_string_lossy().into_owned();
        let mut config_path = dev_container.config_path.to_string_lossy().into_owned();

        #[cfg(unix)]
        if is_wsl()? {
            debug!("WSL detected");
            workspace_path = wslpath2::convert(
                workspace_path.as_str(),
                None,
                wslpath2::Conversion::WslToWindows,
                true,
            )
            .map_err(|error| {
                eyre!("Error while getting wslpath: {error} (path: {workspace_path:?})")
            })?;
            config_path = wslpath2::convert(
                config_path.as_str(),
                None,
                wslpath2::Conversion::WslToWindows,
                true,
            )
            .map_err(|error| {
                eyre!("Error while getting wslpath: {error} (path: {config_path:?})")
            })?;
        }

        #[cfg(windows)]
        {
            workspace_path = workspace_path.replace("\\\\?\\", "");
            config_path = config_path.replace("\\\\?\\", "");
        }

        let folder_uri = DevcontainerUriJson {
            host_path: workspace_path,
            config_file: FileUriJson::new(config_path.as_str()),
        };
        let json = serde_json::to_string(&folder_uri)?;
        trace!("Folder uri JSON: {json}");

        let uri = format!(
            "vscode-remote://dev-container+{}{container_folder}",
            hex::encode(json.as_bytes())
        );
        args.push(OsString::from("--folder-uri"));
        args.push(OsString::from(uri));

        exec_code(args, dry_run, command)
            .wrap_err_with(|| "Error opening vscode using dev container...")
    }
}

fn container_folder(dev_container: &DevContainer, subfolder: Option<&Path>) -> String {
    let mut folder = dev_container.workspace_path_in_container.clone();
    if let Some(subfolder) = subfolder {
        let subfolder = subfolder.to_string_lossy().replace('\\', "/");
        if !subfolder.is_empty() && subfolder != "." {
            if !folder.ends_with('/') {
                folder.push('/');
            }
            folder.push_str(&subfolder);
        }
    }
    folder
}

#[cfg(unix)]
fn is_wsl() -> Result<bool> {
    let output = Command::new("uname")
        .arg("-a")
        .output()
        .wrap_err("Failed to execute uname")?;
    let uname_output = String::from_utf8(output.stdout)?;
    Ok(
        (uname_output.contains("Microsoft") || uname_output.contains("WSL"))
            && std::env::var("WSLENV").is_ok(),
    )
}