@@ -120,18 +120,23 @@ impl Lock {
120120
121121 if self . dry_run {
122122 self . show_dry_run ( & tools, & target_platforms) ?;
123+ let lockfile = Lockfile :: read ( lockfile_path) ?;
123124 if self . is_unfiltered_lock_run ( ) {
124- let lockfile = Lockfile :: read ( lockfile_path) ?;
125125 let stale_tools = self . stale_entries_if_pruned ( & lockfile, & tools) ;
126126 self . show_stale_prune_message ( lockfile_path, & stale_tools, true ) ?;
127+ } else {
128+ let stale_versions = self . stale_versions_if_pruned ( & lockfile, & tools) ;
129+ self . show_stale_version_prune_message ( lockfile_path, & stale_versions, true ) ?;
127130 }
128131 continue ;
129132 }
130133
131134 // Process tools and update lockfile
132135 let mut lockfile = Lockfile :: read ( lockfile_path) ?;
133136 self . prune_stale_entries_if_needed ( & mut lockfile, & tools) ;
137+ let stale_versions = self . stale_versions_if_pruned ( & lockfile, & tools) ;
134138 self . prune_stale_versions_for_targeted_tools ( & mut lockfile, & tools) ;
139+ self . show_stale_version_prune_message ( lockfile_path, & stale_versions, false ) ?;
135140 let results = self
136141 . process_tools ( & settings, & tools, & target_platforms, & mut lockfile)
137142 . await ?;
@@ -214,6 +219,60 @@ impl Lock {
214219 self . stale_entries_for_selectors ( lockfile, & configured_tools, & configured_backends)
215220 }
216221
222+ fn stale_versions_if_pruned (
223+ & self ,
224+ lockfile : & Lockfile ,
225+ tools : & [ LockTool ] ,
226+ ) -> BTreeMap < String , Vec < String > > {
227+ let mut stale: BTreeMap < String , Vec < String > > = BTreeMap :: new ( ) ;
228+ let mut current_versions: BTreeMap < String , BTreeSet < String > > = BTreeMap :: new ( ) ;
229+ for ( ba, tv) in tools {
230+ current_versions
231+ . entry ( ba. short . clone ( ) )
232+ . or_default ( )
233+ . insert ( tv. version . clone ( ) ) ;
234+ }
235+ for ( short, versions) in & current_versions {
236+ let stale_versions = lockfile. stale_tool_versions ( short, versions) ;
237+ if !stale_versions. is_empty ( ) {
238+ stale. insert ( short. clone ( ) , stale_versions) ;
239+ }
240+ }
241+ stale
242+ }
243+
244+ fn show_stale_version_prune_message (
245+ & self ,
246+ lockfile_path : & Path ,
247+ stale_versions : & BTreeMap < String , Vec < String > > ,
248+ dry_run : bool ,
249+ ) -> Result < ( ) > {
250+ if stale_versions. is_empty ( ) {
251+ return Ok ( ( ) ) ;
252+ }
253+ let total: usize = stale_versions. values ( ) . map ( |v| v. len ( ) ) . sum ( ) ;
254+ let entry_word = if total == 1 { "entry" } else { "entries" } ;
255+ let ( icon, message) = if dry_run {
256+ ( style ( "→" ) . yellow ( ) , "Dry run - would prune" )
257+ } else {
258+ ( style ( "✓" ) . green ( ) , "Pruned" )
259+ } ;
260+ let details: Vec < String > = stale_versions
261+ . iter ( )
262+ . flat_map ( |( short, versions) | versions. iter ( ) . map ( move |v| format ! ( "{short}@{v}" ) ) )
263+ . collect ( ) ;
264+ miseprintln ! (
265+ "{} {} {} stale version {} from {}: {}" ,
266+ icon,
267+ message,
268+ total,
269+ entry_word,
270+ style( display_path( lockfile_path) ) . cyan( ) ,
271+ details. join( ", " )
272+ ) ;
273+ Ok ( ( ) )
274+ }
275+
217276 fn configured_tool_selectors (
218277 & self ,
219278 tools : & [ ( crate :: cli:: args:: BackendArg , crate :: toolset:: ToolVersion ) ] ,
0 commit comments