tldr_cli/commands/
inheritance.rs1use std::path::PathBuf;
25
26use anyhow::Result;
27use clap::Args;
28
29use tldr_core::inheritance::{extract_inheritance, format_dot, format_text, InheritanceOptions};
30use tldr_core::Language;
31
32use crate::output::{OutputFormat, OutputWriter};
33
34#[derive(Debug, Args)]
36pub struct InheritanceArgs {
37 #[arg(default_value = ".")]
39 pub path: PathBuf,
40
41 #[arg(long, short = 'l')]
43 pub lang: Option<Language>,
44
45 #[arg(long, short = 'c')]
47 pub class: Option<String>,
48
49 #[arg(long, short = 'd')]
51 pub depth: Option<usize>,
52
53 #[arg(long)]
55 pub no_patterns: bool,
56
57 #[arg(long)]
59 pub no_external: bool,
60
61 #[arg(long = "output", short = 'o', hide = true, value_parser = parse_inheritance_format)]
63 pub output: Option<InheritanceFormat>,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum InheritanceFormat {
69 Json,
70 Text,
71 Dot,
72}
73
74fn parse_inheritance_format(s: &str) -> Result<InheritanceFormat, String> {
75 match s.to_lowercase().as_str() {
76 "json" => Ok(InheritanceFormat::Json),
77 "text" => Ok(InheritanceFormat::Text),
78 "dot" | "graphviz" => Ok(InheritanceFormat::Dot),
79 _ => Err(format!(
80 "Invalid format '{}'. Expected: json, text, or dot",
81 s
82 )),
83 }
84}
85
86impl InheritanceArgs {
87 pub fn run(&self, format: OutputFormat, quiet: bool) -> Result<()> {
89 let writer = OutputWriter::new(format, quiet);
90
91 writer.progress(&format!(
92 "Analyzing inheritance in {}...",
93 self.path.display()
94 ));
95
96 let options = InheritanceOptions {
98 class_filter: self.class.clone(),
99 depth: self.depth,
100 no_patterns: self.no_patterns,
101 no_external: self.no_external,
102 ..Default::default()
103 };
104
105 let report = extract_inheritance(&self.path, self.lang, &options)?;
107
108 let inh_format = self.output.unwrap_or_else(|| {
110 if writer.is_text() {
111 InheritanceFormat::Text
112 } else {
113 InheritanceFormat::Json
114 }
115 });
116
117 match inh_format {
119 InheritanceFormat::Json => {
120 writer.write(&report)?;
121 }
122 InheritanceFormat::Text => {
123 let text = format_text(&report);
124 writer.write_text(&text)?;
125 }
126 InheritanceFormat::Dot => {
127 let dot = format_dot(&report);
128 writer.write_text(&dot)?;
129 }
130 }
131
132 if !quiet {
134 eprintln!(
135 "Found {} classes in {}ms",
136 report.count, report.scan_time_ms
137 );
138
139 if !report.diamonds.is_empty() {
140 eprintln!(
141 "Warning: {} diamond inheritance pattern(s) detected",
142 report.diamonds.len()
143 );
144 }
145 }
146
147 Ok(())
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_parse_inheritance_format() {
157 assert_eq!(
158 parse_inheritance_format("json").unwrap(),
159 InheritanceFormat::Json
160 );
161 assert_eq!(
162 parse_inheritance_format("text").unwrap(),
163 InheritanceFormat::Text
164 );
165 assert_eq!(
166 parse_inheritance_format("dot").unwrap(),
167 InheritanceFormat::Dot
168 );
169 assert_eq!(
170 parse_inheritance_format("graphviz").unwrap(),
171 InheritanceFormat::Dot
172 );
173 assert_eq!(
174 parse_inheritance_format("DOT").unwrap(),
175 InheritanceFormat::Dot
176 );
177 assert!(parse_inheritance_format("invalid").is_err());
178 }
179}