1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use clap::Parser;

use crate::{
    analyzer::{load_workspace, LoadOptions},
    options::{GeneralOptions, ProjectOptions},
};

use self::{
    dependencies::command::Command as DependenciesCommand,
    orphans::command::Command as OrphansCommand, structure::command::Command as StructureCommand,
};

pub mod dependencies;
pub mod orphans;
pub mod structure;

#[derive(Parser, Clone, PartialEq, Eq, Debug)]
#[command(
    name = "cargo-modules",
    about = "Visualize/analyze a crate's internal structure."
)]
pub enum Command {
    #[command(
        name = "structure",
        about = "Prints a crate's hierarchical structure as a tree."
    )]
    Structure(StructureCommand),

    #[command(
        name = "dependencies",
        about = "Prints a crate's internal dependencies as a graph.",
        after_help = r#"
        If you have xdot installed on your system, you can run this using:
        `cargo modules dependencies | xdot -`
        "#
    )]
    Dependencies(DependenciesCommand),

    #[command(
        name = "orphans",
        about = "Detects unlinked source files within a crate's directory."
    )]
    Orphans(OrphansCommand),
}

impl Command {
    pub(crate) fn sanitize(&mut self) {
        match self {
            Self::Structure(command) => command.sanitize(),
            Self::Dependencies(command) => command.sanitize(),
            Self::Orphans(command) => command.sanitize(),
        }
    }

    pub fn run(self) -> Result<(), anyhow::Error> {
        let general_options = self.general_options();
        let project_options = self.project_options();
        let load_options = self.load_options();

        let (krate, host, vfs) = load_workspace(general_options, project_options, &load_options)?;
        let db = host.raw_database();

        match self {
            #[allow(unused_variables)]
            Self::Structure(command) => command.run(krate, db),
            #[allow(unused_variables)]
            Self::Dependencies(command) => command.run(krate, db),
            #[allow(unused_variables)]
            Self::Orphans(command) => command.run(krate, db, &vfs),
        }
    }

    fn general_options(&self) -> &GeneralOptions {
        match self {
            Self::Structure(command) => &command.options.general,
            Self::Dependencies(command) => &command.options.general,
            Self::Orphans(command) => &command.options.general,
        }
    }

    #[allow(dead_code)]
    fn project_options(&self) -> &ProjectOptions {
        match self {
            Self::Structure(command) => &command.options.project,
            Self::Dependencies(command) => &command.options.project,
            Self::Orphans(command) => &command.options.project,
        }
    }

    #[allow(dead_code)]
    fn load_options(&self) -> LoadOptions {
        match self {
            Self::Structure(command) => command.load_options(),
            Self::Dependencies(command) => command.load_options(),
            Self::Orphans(command) => command.load_options(),
        }
    }
}