sdkman-cli-native 0.5.2

Native CLI subcommand components for SDKMAN! written in Rust. Use the binaries generated by this project in the sdk wrapper shell function from the sdkman-cli project.
Documentation
use std::fs::{create_dir_all, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use symlink::symlink_dir;

use tempfile::{Builder, TempDir};

pub struct TestCandidate {
    pub name: &'static str,
    pub versions: Vec<&'static str>,
    pub current_version: &'static str,
}

#[derive(Default)]
pub struct VirtualEnv {
    pub cli_version: String,
    pub native_version: String,
    pub candidate: Option<TestCandidate>,
}

pub fn virtual_env(virtual_env: VirtualEnv) -> TempDir {
    let sdkman_dir = prepare_sdkman_dir();
    let var_path = Path::new("var");

    // script version file
    write_file(
        sdkman_dir.path(),
        var_path,
        "version",
        virtual_env.cli_version,
    );

    // native version file
    write_file(
        sdkman_dir.path(),
        var_path,
        "version_native",
        virtual_env.native_version,
    );

    // candidates file
    write_file(
        sdkman_dir.path(),
        Path::new("var"),
        "candidates",
        virtual_env
            .candidate
            .as_ref()
            .map_or_else(|| "", |c| c.name)
            .to_string(),
    );

    if let Some(candidate) = virtual_env.candidate {
        for version in candidate.versions {
            let location = format!("candidates/{}/{}/bin/", candidate.name, version);
            let content = format!(
                "\
#!/bin/bash
echo Running {} {}
",
                candidate.name, version
            );
            write_file(
                sdkman_dir.path(),
                Path::new(&location),
                candidate.name,
                content,
            );
        }

        let version_location = PathBuf::from(format!(
            "candidates/{}/{}",
            candidate.name, candidate.current_version
        ));
        let current_link_location = PathBuf::from(format!("candidates/{}/current", candidate.name));
        let absolute_version = sdkman_dir.path().join(version_location.as_path());
        let absolute_current_link = sdkman_dir.path().join(current_link_location.as_path());
        symlink_dir(absolute_version, absolute_current_link)
            .expect("cannot create current symlink");
    }

    return sdkman_dir;
}

pub fn prepare_sdkman_dir() -> TempDir {
    Builder::new()
        .prefix(".sdkman-")
        .tempdir()
        .expect("could not prepare SDKMAN_DIR")
}

pub fn write_file(
    temp_dir: &Path,
    relative_path: &Path,
    file_name: &str,
    content: String,
) -> PathBuf {
    let absolute_path = temp_dir.join(relative_path);
    create_dir_all(absolute_path.to_owned()).expect("could not create nested dirs");

    let file_path = absolute_path.join(file_name);
    let mut file = File::create(&file_path).expect("could not create file");
    write!(file, "{}", content.to_string()).expect("could not write to file");

    file_path
}