1use clap::Parser;
6use ra_ap_hir::db::HirDatabase;
7use ra_ap_ide::RootDatabase;
8
9use crate::{
10 analyzer::{LoadOptions, load_workspace},
11 options::{GeneralOptions, ProjectOptions},
12};
13
14use self::{
15 dependencies::command::Command as DependenciesCommand,
16 orphans::command::Command as OrphansCommand, structure::command::Command as StructureCommand,
17};
18
19pub mod dependencies;
20pub mod orphans;
21pub mod structure;
22
23#[derive(Parser, Clone, PartialEq, Eq, Debug)]
24#[command(
25 name = "cargo-modules",
26 bin_name = "cargo-modules",
27 about = "Visualize/analyze a crate's internal structure."
28)]
29pub enum Command {
30 #[command(
31 name = "structure",
32 about = "Prints a crate's hierarchical structure as a tree."
33 )]
34 Structure(StructureCommand),
35
36 #[command(
37 name = "dependencies",
38 about = "Prints a crate's internal dependencies as a graph.",
39 after_help = r#"
40 If you have xdot installed on your system, you can run this using:
41 `cargo modules dependencies | xdot -`
42 "#
43 )]
44 Dependencies(DependenciesCommand),
45
46 #[command(
47 name = "orphans",
48 about = "Detects unlinked source files within a crate's directory."
49 )]
50 Orphans(OrphansCommand),
51}
52
53impl Command {
54 pub(crate) fn sanitize(&mut self) {
55 match self {
56 Self::Structure(command) => command.sanitize(),
57 Self::Dependencies(command) => command.sanitize(),
58 Self::Orphans(command) => command.sanitize(),
59 }
60 }
61
62 pub fn run(self) -> Result<(), anyhow::Error> {
63 let general_options = self.general_options();
64 let project_options = self.project_options();
65 let load_options = self.load_options();
66
67 let (krate, host, vfs, edition) =
68 load_workspace(general_options, project_options, &load_options)?;
69 let db: &RootDatabase = host.raw_database();
70 let hir_db: &dyn HirDatabase = db;
71
72 ra_ap_hir::attach_db(hir_db, || match self {
73 #[allow(unused_variables)]
74 Self::Structure(command) => command.run(krate, db, edition),
75 #[allow(unused_variables)]
76 Self::Dependencies(command) => command.run(krate, db, edition),
77 #[allow(unused_variables)]
78 Self::Orphans(command) => command.run(krate, db, &vfs, edition),
79 })
80 }
81
82 fn general_options(&self) -> &GeneralOptions {
83 match self {
84 Self::Structure(command) => &command.options.general,
85 Self::Dependencies(command) => &command.options.general,
86 Self::Orphans(command) => &command.options.general,
87 }
88 }
89
90 #[allow(dead_code)]
91 fn project_options(&self) -> &ProjectOptions {
92 match self {
93 Self::Structure(command) => &command.options.project,
94 Self::Dependencies(command) => &command.options.project,
95 Self::Orphans(command) => &command.options.project,
96 }
97 }
98
99 #[allow(dead_code)]
100 fn load_options(&self) -> LoadOptions {
101 match self {
102 Self::Structure(command) => command.load_options(),
103 Self::Dependencies(command) => command.load_options(),
104 Self::Orphans(command) => command.load_options(),
105 }
106 }
107}