vdev/commands/
crate_versions.rs1use std::{
2 collections::{HashMap, HashSet},
3 process::Command,
4};
5
6use anyhow::Result;
7use clap::Args;
8use itertools::Itertools as _;
9use regex::Regex;
10
11use crate::{app::CommandExt as _, utils};
12
13#[derive(Args, Debug)]
15#[command()]
16pub struct Cli {
17 #[arg(long)]
19 all: bool,
20
21 #[arg(short = 'F', long)]
23 feature: Vec<String>,
24}
25
26impl Cli {
27 pub fn exec(self) -> Result<()> {
28 let re_crate = Regex::new(r" (\S+) v([0-9.]+)").unwrap();
29 let mut versions: HashMap<String, HashSet<String>> = HashMap::default();
30
31 for line in Command::new("cargo")
32 .arg("tree")
33 .features(&self.feature)
34 .check_output()?
35 .lines()
36 {
37 if let Some(captures) = re_crate.captures(line) {
38 let package = &captures[1];
39 let version = &captures[2];
40 versions
41 .entry(package.into())
42 .or_default()
43 .insert(version.into());
44 }
45 }
46
47 if !self.all {
48 versions.retain(|_, versions| versions.len() > 1);
49 }
50
51 let width = versions.keys().map(String::len).max().unwrap_or(0).max(7);
52 if *utils::IS_A_TTY {
53 println!("{:width$} Version(s)", "Package");
54 println!("{:width$} ----------", "-------");
55 }
56
57 for (package, versions) in versions {
58 let versions = versions.iter().join(" ");
59 println!("{package:width$} {versions}");
60 }
61
62 Ok(())
63 }
64}