1use std::collections::HashMap;
2
3use clap::Args;
4use itertools::Itertools;
5use lux_lib::{
6 config::Config, lockfile::LocalPackage, lua_version::LuaVersion, package::PackageVersion,
7 remote_package_db::RemotePackageDB, workspace::Workspace,
8};
9
10use miette::{IntoDiagnostic, Result};
11use text_trees::{FormatCharacters, StringTreeNode, TreeFormatting};
12
13use crate::{args::OutputFormat, workspace::sync_dependencies_if_locked};
14
15#[derive(Args)]
16pub struct Outdated {
17 #[arg(long, default_value = "text", value_enum, ignore_case = true)]
18 output_format: OutputFormat,
19}
20
21pub async fn outdated(outdated_data: Outdated, config: Config) -> Result<()> {
24 let workspace = Workspace::current()?;
25 let tree = match &workspace {
26 Some(project) => {
27 sync_dependencies_if_locked(project, &config).await?;
29 project.tree(&config)?
30 }
31 None => {
32 let lua_version = LuaVersion::from(&config)?.clone();
33 config.user_tree(lua_version)?
34 }
35 };
36
37 let package_db = RemotePackageDB::from_config(&config).await?;
38
39 let rock_list = tree.as_rock_list()?;
44 let rock_list = rock_list
45 .iter()
46 .map(|rock| {
47 rock.to_package()
48 .has_update(&package_db)
49 .map(|mb_version| mb_version.map(|version| (rock, version)))
50 })
51 .filter_map_ok(|mb_tuple| mb_tuple)
52 .try_collect::<_, Vec<(&LocalPackage, PackageVersion)>, _>()?;
53
54 let rock_list = rock_list
55 .iter()
56 .sorted_by_key(|(rock, _)| rock.name().to_owned())
57 .into_group_map_by(|(rock, _)| rock.name().to_owned());
58
59 match outdated_data.output_format {
60 OutputFormat::Json => {
61 let jsonified_rock_list = rock_list
62 .iter()
63 .map(|(key, values)| {
64 (
65 key,
66 values
67 .iter()
68 .map(|(k, v)| (k.version().to_string(), v.to_string()))
69 .collect::<HashMap<_, _>>(),
70 )
71 })
72 .collect::<HashMap<_, _>>();
73
74 println!(
75 "{}",
76 serde_json::to_string(&jsonified_rock_list).into_diagnostic()?
77 );
78 }
79 OutputFormat::Text => {
80 let formatting = TreeFormatting::dir_tree(FormatCharacters::box_chars());
81
82 for (rock_name, updates) in rock_list {
83 let mut tree = StringTreeNode::new(rock_name.to_string());
84
85 for (rock, latest_version) in updates {
86 tree.push(format!("{} => {}", rock.version(), latest_version));
87 }
88
89 println!(
90 "{}",
91 tree.to_string_with_format(&formatting).into_diagnostic()?
92 );
93 }
94 }
95 }
96
97 Ok(())
98}