diff --git a/src/cli/plugins/ls.rs b/src/cli/plugins/ls.rs index ffdaa617b..0cc819519 100644 --- a/src/cli/plugins/ls.rs +++ b/src/cli/plugins/ls.rs @@ -1,13 +1,10 @@ -use std::collections::BTreeSet; -use std::sync::Arc; - use eyre::Result; use rayon::prelude::*; use tabled::{Table, Tabled}; use crate::config::Config; -use crate::forge::asdf::Asdf; use crate::plugins; +use crate::plugins::asdf_plugin::AsdfPlugin; use crate::plugins::PluginType; use crate::ui::table; @@ -47,34 +44,46 @@ pub struct PluginsLs { impl PluginsLs { pub fn run(self, config: &Config) -> Result<()> { - let mut tools = plugins::list().into_iter().collect::>(); + let mut tools = plugins::list2()?; if self.all { for (plugin, url) in config.get_shorthands() { - let mut ep = Asdf::new(plugin.clone()); + let mut ep = AsdfPlugin::new(plugin.to_string()); ep.repo_url = Some(url.to_string()); - tools.insert(Arc::new(ep)); + tools.insert(ep.name.clone(), Box::new(ep)); } } else if self.user && self.core { } else if self.core { - tools.retain(|p| matches!(p.get_plugin_type(), PluginType::Core)); + tools.retain(|_, p| matches!(p.get_plugin_type(), PluginType::Core)); } else { - tools.retain(|p| matches!(p.get_plugin_type(), PluginType::Asdf)); + tools.retain(|_, p| matches!(p.get_plugin_type(), PluginType::Asdf)); } if self.urls || self.refs { let data = tools .into_par_iter() - .map(|p| { + .map(|(name, p)| { + let remote_url = p.get_remote_url().unwrap_or_else(|e| { + warn!("{name}: {e:?}"); + None + }); + let abbrev_ref = p.current_abbrev_ref().unwrap_or_else(|e| { + warn!("{name}: {e:?}"); + None + }); + let sha_short = p.current_sha_short().unwrap_or_else(|e| { + warn!("{name}: {e:?}"); + None + }); let mut row = Row { - plugin: p.id().to_string(), - url: p.get_remote_url().unwrap_or_default(), + plugin: name, + url: remote_url.unwrap_or_default(), ref_: String::new(), sha: String::new(), }; if p.is_installed() { - row.ref_ = p.current_abbrev_ref().unwrap_or_default(); - row.sha = p.current_sha_short().unwrap_or_default(); + row.ref_ = abbrev_ref.unwrap_or_default(); + row.sha = sha_short.unwrap_or_default(); } row }) @@ -83,7 +92,7 @@ impl PluginsLs { table::default_style(&mut table, false); miseprintln!("{table}"); } else { - for tool in tools { + for tool in tools.values() { miseprintln!("{tool}"); } } diff --git a/src/cli/run.rs b/src/cli/run.rs index 1eef3b2b8..cb10d6082 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashSet}; use std::io::Write; use std::iter::once; #[cfg(unix)] -use std::os::unix::prelude::ExitStatusExt; +use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; use std::process::{exit, Stdio}; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/src/forge/asdf.rs b/src/forge/asdf.rs index 8869ee90a..e6a09a24d 100644 --- a/src/forge/asdf.rs +++ b/src/forge/asdf.rs @@ -501,16 +501,6 @@ impl Forge for Asdf { git.get_remote_url().or_else(|| self.repo_url.clone()) } - fn current_sha_short(&self) -> Result { - let git = Git::new(self.plugin_path.to_path_buf()); - git.current_sha_short() - } - - fn current_abbrev_ref(&self) -> Result { - let git = Git::new(self.plugin_path.to_path_buf()); - git.current_abbrev_ref() - } - fn is_installed(&self) -> bool { self.plugin_path.exists() } diff --git a/src/forge/mod.rs b/src/forge/mod.rs index e4d1beb3a..7c2f28634 100644 --- a/src/forge/mod.rs +++ b/src/forge/mod.rs @@ -288,12 +288,6 @@ pub trait Forge: Debug + Send + Sync { fn get_remote_url(&self) -> Option { None } - fn current_sha_short(&self) -> eyre::Result { - Ok(String::from("")) - } - fn current_abbrev_ref(&self) -> eyre::Result { - Ok(String::from("")) - } fn is_installed(&self) -> bool { true } diff --git a/src/git.rs b/src/git.rs index c63391e3b..0e2792ef0 100644 --- a/src/git.rs +++ b/src/git.rs @@ -1,3 +1,4 @@ +use std::fmt::Debug; use std::path::PathBuf; use duct::Expression; @@ -217,6 +218,12 @@ fn get_git_version() -> Result { Ok(version.trim().into()) } +impl Debug for Git { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Git").field("dir", &self.dir).finish() + } +} + // #[cfg(test)] // mod tests { // use tempfile::tempdir; diff --git a/src/plugins/asdf_plugin.rs b/src/plugins/asdf_plugin.rs new file mode 100644 index 000000000..231c7acb4 --- /dev/null +++ b/src/plugins/asdf_plugin.rs @@ -0,0 +1,69 @@ +use crate::config::Settings; +use crate::dirs; +use crate::git::Git; +use crate::plugins::{Plugin, PluginList, PluginType}; +use rayon::prelude::*; +use xx::file; + +#[derive(Debug)] +pub struct AsdfPlugin { + pub name: String, + pub repo: Git, + pub repo_url: Option, +} + +impl AsdfPlugin { + pub fn new(name: String) -> Self { + let dir = dirs::PLUGINS.join(&name); + Self { + name, + repo: Git::new(dir), + repo_url: None, + } + } + + pub fn list() -> eyre::Result { + let settings = Settings::get(); + Ok(file::ls(*dirs::PLUGINS)? + .into_par_iter() + .map(|dir| { + let name = dir.file_name().unwrap().to_string_lossy().to_string(); + Box::new(AsdfPlugin::new(name)) as Box + }) + .filter(|p| !settings.disable_tools.contains(p.name())) + .collect()) + } +} + +impl Plugin for AsdfPlugin { + fn name(&self) -> &str { + &self.name + } + + fn get_plugin_type(&self) -> PluginType { + PluginType::Asdf + } + + fn get_remote_url(&self) -> eyre::Result> { + let url = self.repo.get_remote_url(); + Ok(url.or(self.repo_url.clone())) + } + + fn current_abbrev_ref(&self) -> eyre::Result> { + if !self.is_installed() { + return Ok(None); + } + self.repo.current_abbrev_ref().map(Some) + } + + fn current_sha_short(&self) -> eyre::Result> { + if !self.is_installed() { + return Ok(None); + } + self.repo.current_sha_short().map(Some) + } + + fn is_installed(&self) -> bool { + self.repo.exists() + } +} diff --git a/src/plugins/core/mod.rs b/src/plugins/core/mod.rs index e1e8a8162..8d2f29733 100644 --- a/src/plugins/core/mod.rs +++ b/src/plugins/core/mod.rs @@ -21,6 +21,7 @@ use crate::plugins::core::java::JavaPlugin; use crate::plugins::core::node::NodePlugin; use crate::plugins::core::ruby::RubyPlugin; use crate::plugins::core::zig::ZigPlugin; +use crate::plugins::{Plugin, PluginList, PluginType}; use crate::timeout::run_with_timeout; use crate::toolset::ToolVersion; @@ -59,6 +60,15 @@ pub struct CorePlugin { } impl CorePlugin { + pub fn list() -> PluginList { + let settings = Settings::get(); + CORE_PLUGINS + .iter() + .map(|f| Box::new(CorePlugin::new(f.name().to_string().into())) as Box) + .filter(|p| !settings.disable_tools.contains(p.name())) + .collect() + } + pub fn new(fa: ForgeArg) -> Self { Self { remote_version_cache: CacheManager::new( @@ -96,3 +106,29 @@ impl CorePlugin { Ok(Some(versions)) } } + +impl Plugin for CorePlugin { + fn name(&self) -> &str { + &self.fa.name + } + + fn get_plugin_type(&self) -> PluginType { + PluginType::Core + } + + fn get_remote_url(&self) -> Result> { + Ok(None) + } + + fn current_abbrev_ref(&self) -> Result> { + Ok(None) + } + + fn current_sha_short(&self) -> Result> { + Ok(None) + } + + fn is_installed(&self) -> bool { + true + } +} diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 090781e51..5ae227b2f 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,4 +1,5 @@ -use std::fmt::Debug; +use std::collections::BTreeMap; +use std::fmt::{Debug, Display}; use once_cell::sync::Lazy; use regex::Regex; @@ -8,7 +9,10 @@ pub use script_manager::{Script, ScriptManager}; use crate::cli::args::ForgeArg; use crate::forge; use crate::forge::{AForge, ForgeList, ForgeType}; +use crate::plugins::asdf_plugin::AsdfPlugin; +use crate::plugins::core::CorePlugin; +pub mod asdf_plugin; pub mod core; pub mod mise_plugin_toml; pub mod script_manager; @@ -37,6 +41,16 @@ pub fn list() -> ForgeList { .collect() } +pub fn list2() -> eyre::Result { + let core = CorePlugin::list() + .into_iter() + .map(|p| (p.name().to_string(), p)); + let asdf = AsdfPlugin::list()? + .into_iter() + .map(|p| (p.name().to_string(), p)); + Ok(core.chain(asdf).collect()) +} + pub fn list_external() -> ForgeList { list() .into_iter() @@ -44,12 +58,51 @@ pub fn list_external() -> ForgeList { .collect() } +pub type APlugin = Box; +pub type PluginMap = BTreeMap; +pub type PluginList = Vec; + +pub trait Plugin: Debug + Send { + fn name(&self) -> &str; + fn get_plugin_type(&self) -> PluginType; + fn get_remote_url(&self) -> eyre::Result>; + fn current_abbrev_ref(&self) -> eyre::Result>; + fn current_sha_short(&self) -> eyre::Result>; + fn is_installed(&self) -> bool; +} + +impl Ord for APlugin { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.name().cmp(other.name()) + } +} + +impl PartialOrd for APlugin { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for APlugin { + fn eq(&self, other: &Self) -> bool { + self.name() == other.name() + } +} + +impl Eq for APlugin {} + +impl Display for APlugin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name()) + } +} + #[cfg(test)] mod tests { - use crate::forge::asdf::Asdf; use pretty_assertions::assert_str_eq; use test_log::test; + use crate::forge::asdf::Asdf; use crate::forge::Forge; use crate::test::reset;