Skip to main content

cargo_modules/
command.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use 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}