diff --git a/e2e/tools/test_tools_alias b/e2e/tools/test_tools_alias new file mode 100644 index 0000000000..ab49786cbb --- /dev/null +++ b/e2e/tools/test_tools_alias @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +cat <mise.toml +tools.xxx21 = "2" +tools.xxxlts = "lts" + +alias.xxx21 = "asdf:tiny" +[alias.xxxlts] +full = "asdf:tiny" +version = "1.0.1" +EOF + +assert "mise x xxx21 -- rtx-tiny" "2" +assert "mise x xxxlts -- rtx-tiny" "2" diff --git a/src/cli/alias/get.rs b/src/cli/alias/get.rs index fea5cdf8b4..cdfd8ea18c 100644 --- a/src/cli/alias/get.rs +++ b/src/cli/alias/get.rs @@ -20,7 +20,7 @@ impl AliasGet { pub fn run(self) -> Result<()> { let config = Config::try_get()?; match config.get_all_aliases().get(&self.plugin) { - Some(plugin) => match plugin.get(&self.alias) { + Some(alias) => match alias.versions.get(&self.alias) { Some(alias) => { miseprintln!("{alias}"); Ok(()) diff --git a/src/cli/alias/ls.rs b/src/cli/alias/ls.rs index d3fef12390..0bc718a908 100644 --- a/src/cli/alias/ls.rs +++ b/src/cli/alias/ls.rs @@ -36,6 +36,7 @@ impl AliasLs { }) .flat_map(|(fa, aliases)| { aliases + .versions .iter() .filter(|(from, _to)| fa.name != "node" || !from.starts_with("lts/")) .map(|(from, to)| Row { diff --git a/src/config/config_file/mise_toml.rs b/src/config/config_file/mise_toml.rs index 7dfef72bf8..68190ebd98 100644 --- a/src/config/config_file/mise_toml.rs +++ b/src/config/config_file/mise_toml.rs @@ -6,7 +6,7 @@ use eyre::{eyre, WrapErr}; use indexmap::IndexMap; use itertools::Itertools; use once_cell::sync::OnceCell; -use serde::de::Visitor; +use serde::de::{Visitor}; use serde::{de, Deserializer}; use serde_derive::Deserialize; use tera::Context as TeraContext; @@ -18,7 +18,7 @@ use crate::config::config_file::toml::{deserialize_arr, deserialize_path_entry_a use crate::config::config_file::{trust_check, ConfigFile, TaskConfig}; use crate::config::env_directive::{EnvDirective, PathEntry}; use crate::config::settings::SettingsPartial; -use crate::config::AliasMap; +use crate::config::{Alias, AliasMap}; use crate::file::{create_dir_all, display_path}; use crate::task::Task; use crate::tera::{get_tera, BASE_CONTEXT}; @@ -39,7 +39,7 @@ pub struct MiseToml { env: EnvList, #[serde(default, deserialize_with = "deserialize_arr")] env_path: Vec, - #[serde(default, deserialize_with = "deserialize_alias")] + #[serde(default)] alias: AliasMap, #[serde(skip)] doc: OnceCell, @@ -121,6 +121,7 @@ impl MiseToml { self.alias .entry(fa.clone()) .or_default() + .versions .insert(from.into(), to.into()); self.doc_mut()? .entry("alias") @@ -155,9 +156,9 @@ impl MiseToml { } } if let Some(aliases) = self.alias.get_mut(fa) { - aliases.remove(from); - if aliases.is_empty() { - self.alias.remove(fa); + aliases.versions.swap_remove(from); + if aliases.versions.is_empty() { + self.alias.swap_remove(fa); } } Ok(()) @@ -382,16 +383,19 @@ impl ConfigFile for MiseToml { .clone() .iter() .map(|(k, v)| { - let k = k.clone(); - let v: Result, eyre::Error> = v + let versions = v .clone() + .versions .into_iter() .map(|(k, v)| { let v = self.parse_template(&v)?; - Ok((k, v)) + Ok::<(String, String), eyre::Report>((k, v)) }) - .collect(); - v.map(|v| (k, v)) + .collect::>>()?; + Ok((k.clone(), Alias{ + full: v.full.clone(), + versions, + })) }) .collect() } @@ -906,35 +910,58 @@ impl<'de> de::Deserialize<'de> for BackendArg { } } -fn deserialize_alias<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - struct AliasMapVisitor; +impl<'de> de::Deserialize<'de> for Alias { + fn deserialize(deserializer: D) -> std::result::Result + where + D: de::Deserializer<'de>, + { + struct AliasVisitor; - impl<'de> Visitor<'de> for AliasMapVisitor { - type Value = AliasMap; - fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { - formatter.write_str("alias table") - } + impl<'de> Visitor<'de> for AliasVisitor { + type Value = Alias; + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("alias") + } - fn visit_map(self, mut map: M) -> std::result::Result - where - M: de::MapAccess<'de>, - { - let mut aliases = AliasMap::new(); - while let Some(plugin) = map.next_key::()? { - let fa: BackendArg = plugin.as_str().into(); - let plugin_aliases = aliases.entry(fa).or_default(); - for (from, to) in map.next_value::>()? { - plugin_aliases.insert(from, to); + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(Alias{ + full: Some(v.to_string()), + ..Default::default() + }) + } + + fn visit_map(self, mut map: M) -> Result + where + M: de::MapAccess<'de>, + { + let mut full = None; + let mut versions = IndexMap::new(); + while let Some(key) = map.next_key::()? { + match key.as_str() { + "full" => { + full = Some(map.next_value()?); + } + "versions" => { + versions = map.next_value()?; + } + _ => { + warn!("deprecated: tool version aliases should now be nested in `alias..versions. = `"); + versions.insert(key, map.next_value()?); + } + } } + Ok(Alias{ + full, + versions, + }) } - Ok(aliases) } - } - deserializer.deserialize_map(AliasMapVisitor) + deserializer.deserialize_any(AliasVisitor) + } } #[cfg(test)] diff --git a/src/config/mod.rs b/src/config/mod.rs index a86bf827fb..e4722fea85 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -34,7 +34,7 @@ pub mod tracking; pub use settings::SETTINGS; -type AliasMap = BTreeMap>; +type AliasMap = IndexMap; type ConfigMap = IndexMap>; type EnvWithSources = IndexMap; @@ -52,6 +52,12 @@ pub struct Config { tool_request_set: OnceCell, } +#[derive(Debug, Clone, Default)] +pub struct Alias { + pub full: Option, + pub versions: IndexMap, +} + pub static CONFIG: Lazy> = Lazy::new(Config::get); static _CONFIG: RwLock>> = RwLock::new(None); @@ -188,7 +194,7 @@ impl Config { pub fn resolve_alias(&self, backend: &dyn Backend, v: &str) -> Result { if let Some(plugin_aliases) = self.aliases.get(backend.fa()) { - if let Some(alias) = plugin_aliases.get(v) { + if let Some(alias) = plugin_aliases.versions.get(v) { return Ok(alias.clone()); } } @@ -212,15 +218,20 @@ impl Config { .collect(); for (fa, plugin_aliases) in plugin_aliases { for (from, to) in plugin_aliases { - aliases.entry(fa.clone()).or_default().insert(from, to); + aliases + .entry(fa.clone()) + .or_default() + .versions + .insert(from, to); } } for (plugin, plugin_aliases) in &self.aliases { - for (from, to) in plugin_aliases { + for (from, to) in &plugin_aliases.versions { aliases .entry(plugin.clone()) .or_default() + .versions .insert(from.clone(), to.clone()); } } @@ -733,8 +744,8 @@ fn load_aliases(config_files: &ConfigMap) -> Result { for config_file in config_files.values() { for (plugin, plugin_aliases) in config_file.aliases()? { - for (from, to) in plugin_aliases { - aliases.entry(plugin.clone()).or_default().insert(from, to); + for (from, to) in plugin_aliases.versions { + aliases.entry(plugin.clone()).or_default().versions.insert(from, to); } } } diff --git a/src/runtime_symlinks.rs b/src/runtime_symlinks.rs index ca613a7c9a..9f1aaed42d 100644 --- a/src/runtime_symlinks.rs +++ b/src/runtime_symlinks.rs @@ -1,9 +1,8 @@ -use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::sync::Arc; use crate::backend::{backend_meta, Backend}; -use crate::config::Config; +use crate::config::{Alias, Config}; use crate::file::make_symlink_or_file; use crate::plugins::VERSION_REGEX; use crate::{backend, file}; @@ -59,10 +58,11 @@ fn list_symlinks(config: &Config, backend: Arc) -> Result