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