use std::collections::HashSet;
use crate::cli::args::ToolArg;
use crate::config::Config;
use crate::toolset::outdated_info::OutdatedInfo;
use crate::toolset::{ConfigScope, ResolveOptions, ToolsetBuilder};
use crate::ui::table;
use eyre::Result;
use indexmap::IndexMap;
use tabled::settings::Remove;
use tabled::settings::location::ByColumnName;
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct Outdated {
#[clap(value_name = "TOOL@VERSION", verbatim_doc_comment)]
pub tool: Vec<ToolArg>,
#[clap(short = 'J', long, verbatim_doc_comment)]
pub json: bool,
#[clap(long, short = 'l', verbatim_doc_comment)]
pub bump: bool,
#[clap(long, verbatim_doc_comment)]
pub local: bool,
#[clap(long)]
pub no_header: bool,
}
impl Outdated {
pub async fn run(self) -> Result<()> {
let config = Config::get().await?;
let scope = if self.local {
ConfigScope::LocalOnly
} else {
ConfigScope::All
};
let mut ts = ToolsetBuilder::new()
.with_args(&self.tool)
.with_scope(scope)
.build(&config)
.await?;
let tool_set = self
.tool
.iter()
.map(|t| t.ba.clone())
.collect::<HashSet<_>>();
ts.versions
.retain(|_, tvl| tool_set.is_empty() || tool_set.contains(&tvl.backend));
let outdated = ts
.list_outdated_versions(&config, self.bump, &ResolveOptions::default())
.await;
self.display(outdated).await?;
Ok(())
}
async fn display(&self, outdated: Vec<OutdatedInfo>) -> Result<()> {
match self.json {
true => self.display_json(outdated)?,
false => self.display_table(outdated)?,
}
Ok(())
}
fn display_table(&self, outdated: Vec<OutdatedInfo>) -> Result<()> {
if outdated.is_empty() {
info!("All tools are up to date");
if !self.bump {
hint!(
"outdated_bump",
r#"By default, `mise outdated` only shows versions that match your config. Use `mise outdated --bump` to see all new versions."#,
""
);
}
return Ok(());
}
let mut table = tabled::Table::new(outdated);
if !self.bump {
table.with(Remove::column(ByColumnName::new("bump")));
}
table::default_style(&mut table, self.no_header);
miseprintln!("{table}");
Ok(())
}
fn display_json(&self, outdated: Vec<OutdatedInfo>) -> Result<()> {
let mut map = IndexMap::new();
for o in outdated {
map.insert(o.name.to_string(), o);
}
miseprintln!("{}", serde_json::to_string_pretty(&map)?);
Ok(())
}
}
static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
$ <bold>mise outdated</bold>
Plugin Requested Current Latest
python 3.11 3.11.0 3.11.1
node 20 20.0.0 20.1.0
$ <bold>mise outdated node</bold>
Plugin Requested Current Latest
node 20 20.0.0 20.1.0
$ <bold>mise outdated --json</bold>
{"python": {"requested": "3.11", "current": "3.11.0", "latest": "3.11.1"}, ...}
$ <bold>mise outdated --local</bold>
Plugin Requested Current Latest
node 20 20.0.0 20.1.0
"#
);