rtx-cli-test 1.2.6

Polyglot runtime manager (asdf rust clone)
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::path::Path;
use std::sync::Arc;

use color_eyre::eyre::Result;
use indexmap::IndexMap;

use rtxrc::RTXFile;
use tool_versions::ToolVersions;

use crate::cli::args::runtime::RuntimeArg;
use crate::config::Config;
use crate::config::PluginSource;
use crate::env;
use crate::errors::Error::VersionNotInstalled;
use crate::plugins::{Plugin, PluginName};
use crate::runtimes::RuntimeVersion;

pub mod legacy_version;
pub mod rtxrc;
pub mod tool_versions;

#[derive(Debug, PartialEq)]
pub enum ConfigFileType {
    RtxRc,
    ToolVersions,
    LegacyVersion,
}

pub trait ConfigFile: Debug + Display + Send {
    fn get_type(&self) -> ConfigFileType;
    fn get_path(&self) -> &Path;
    fn source(&self) -> PluginSource;
    fn plugins(&self) -> IndexMap<PluginName, Vec<String>>;
    fn env(&self) -> HashMap<String, String>;
    fn remove_plugin(&mut self, plugin_name: &PluginName);
    fn add_version(&mut self, plugin_name: &PluginName, version: &str);
    fn replace_versions(&mut self, plugin_name: &PluginName, versions: &[String]);
    fn save(&self) -> Result<()>;
    fn dump(&self) -> String;
}

impl dyn ConfigFile {
    pub fn add_runtimes(
        &mut self,
        config: &Config,
        runtimes: &[RuntimeArg],
        fuzzy: bool,
    ) -> Result<()> {
        let mut runtime_map: HashMap<PluginName, Vec<String>> = HashMap::new();
        for runtime in runtimes {
            let plugin = Plugin::load_ensure_installed(&runtime.plugin, &config.settings)?;
            let latest = plugin
                .latest_version(&runtime.version)?
                .unwrap_or_else(|| runtime.version.clone());
            let rtv = RuntimeVersion::new(Arc::new(plugin), &latest);
            if !rtv.ensure_installed(config)? {
                return Err(VersionNotInstalled(rtv.plugin.name.clone(), rtv.version))?;
            };
            runtime_map
                .entry(rtv.plugin.name.clone())
                .or_default()
                .push(if fuzzy {
                    runtime.version.to_string()
                } else {
                    rtv.version.to_string()
                });
        }
        for (plugin, versions) in runtime_map {
            self.replace_versions(&plugin, &versions);
        }

        Ok(())
    }
}

pub fn init(path: &Path) -> Box<dyn ConfigFile> {
    if path.ends_with(".rtxrc") || path.ends_with(".rtxrc.toml") {
        return Box::new(RTXFile::init(path));
    } else if path.ends_with(env::RTX_DEFAULT_TOOL_VERSIONS_FILENAME.as_str()) {
        return Box::new(ToolVersions::init(path));
    }

    panic!("Unknown config file type: {}", path.display());
}

pub fn parse(path: &Path) -> Result<Box<dyn ConfigFile>> {
    match detect_config_file_type(path) {
        Some(ConfigFileType::RtxRc) => Ok(Box::new(RTXFile::from_file(path)?)),
        Some(ConfigFileType::ToolVersions) => Ok(Box::new(ToolVersions::from_file(path)?)),
        #[allow(clippy::box_default)]
        _ => Ok(Box::new(RTXFile::default())),
    }
}

fn detect_config_file_type(path: &Path) -> Option<ConfigFileType> {
    match path.file_name().unwrap().to_str().unwrap() {
        ".rtxrc" | ".rtxrc.toml" | "config.toml" => Some(ConfigFileType::RtxRc),
        f if env::RTX_DEFAULT_TOOL_VERSIONS_FILENAME.as_str() == f => {
            Some(ConfigFileType::ToolVersions)
        }
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_detect_config_file_type() {
        assert_eq!(
            detect_config_file_type(Path::new("/foo/bar/.rtxrc")),
            Some(ConfigFileType::RtxRc)
        );
        assert_eq!(
            detect_config_file_type(Path::new("/foo/bar/.rtxrc.toml")),
            Some(ConfigFileType::RtxRc)
        );
        assert_eq!(
            detect_config_file_type(Path::new("/foo/bar/.tool-versions")),
            Some(ConfigFileType::ToolVersions)
        );
        assert_eq!(
            detect_config_file_type(Path::new("/foo/bar/.tool-versions.toml")),
            None
        );
    }
}