tldr_cli/commands/
hubs.rs1use std::path::PathBuf;
20
21use anyhow::Result;
22use clap::{Args, ValueEnum};
23
24use tldr_core::analysis::hubs::{compute_hub_report, HubAlgorithm};
25use tldr_core::callgraph::{build_forward_graph, build_reverse_graph, collect_nodes};
26use tldr_core::{build_project_call_graph, Language};
27
28use crate::output::{format_hubs_text, OutputFormat, OutputWriter};
29
30#[derive(Debug, Clone, Copy, Default, ValueEnum)]
32pub enum AlgorithmArg {
33 #[default]
35 All,
36 Indegree,
38 Outdegree,
40 Pagerank,
42 Betweenness,
44}
45
46impl From<AlgorithmArg> for HubAlgorithm {
47 fn from(arg: AlgorithmArg) -> Self {
48 match arg {
49 AlgorithmArg::All => HubAlgorithm::All,
50 AlgorithmArg::Indegree => HubAlgorithm::InDegree,
51 AlgorithmArg::Outdegree => HubAlgorithm::OutDegree,
52 AlgorithmArg::Pagerank => HubAlgorithm::PageRank,
53 AlgorithmArg::Betweenness => HubAlgorithm::Betweenness,
54 }
55 }
56}
57
58#[derive(Debug, Args)]
60pub struct HubsArgs {
61 #[arg(default_value = ".")]
63 pub path: PathBuf,
64
65 #[arg(long, default_value = "10")]
67 pub top: usize,
68
69 #[arg(long, value_enum, default_value = "all")]
71 pub algorithm: AlgorithmArg,
72
73 #[arg(long)]
75 pub threshold: Option<f64>,
76
77 #[arg(long, short = 'l')]
79 pub lang: Option<Language>,
80}
81
82impl HubsArgs {
83 pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
85 let writer = OutputWriter::new(format, quiet);
86
87 if !self.path.exists() {
89 anyhow::bail!("Path not found: {}", self.path.display());
90 }
91
92 if let Some(thresh) = self.threshold {
94 if !(0.0..=1.0).contains(&thresh) {
95 anyhow::bail!("Threshold must be between 0.0 and 1.0, got {}", thresh);
96 }
97 }
98
99 let language = self
101 .lang
102 .unwrap_or_else(|| Language::from_directory(&self.path).unwrap_or(Language::Python));
103
104 writer.progress(&format!(
105 "Building call graph for {} ({:?})...",
106 self.path.display(),
107 language
108 ));
109
110 let graph = build_project_call_graph(&self.path, language, None, true)?;
112
113 writer.progress("Computing hub centrality metrics...");
114
115 let forward = build_forward_graph(&graph);
117 let reverse = build_reverse_graph(&graph);
118 let nodes = collect_nodes(&graph);
119
120 let report = compute_hub_report(
122 &nodes,
123 &forward,
124 &reverse,
125 self.algorithm.into(),
126 self.top,
127 self.threshold,
128 );
129
130 if writer.is_text() {
132 let text = format_hubs_text(&report);
133 writer.write_text(&text)?;
134 } else {
135 writer.write(&report)?;
136 }
137
138 Ok(())
139 }
140}