Skip to main content

wapm_cli/commands/
list.rs

1//! Subcommand for inspecting installed packages and commands
2
3use crate::config;
4use crate::data::lock::lockfile::{CommandMap, ModuleMap};
5use crate::dataflow::lockfile_packages::LockfileResult;
6use prettytable::{format, Table};
7use std::fmt::Write as _;
8use structopt::StructOpt;
9
10#[derive(StructOpt, Debug)]
11pub struct ListOpt {
12    /// List just the globally installed packages
13    #[structopt(short = "g", long = "global")]
14    global: bool,
15
16    /// List both locally and globally installed packages
17    #[structopt(short = "a", long = "all")]
18    all: bool,
19}
20
21pub fn list(options: ListOpt) -> anyhow::Result<()> {
22    let mut local = false;
23    let mut global = false;
24    match (options.global, options.all) {
25        (_, true) => {
26            local = true;
27            global = true;
28        }
29        (true, false) => {
30            global = true;
31        }
32        (false, false) => {
33            local = true;
34        }
35    }
36    let local_start_value = local;
37
38    let mut handle = String::new();
39    if local {
40        let cwd = crate::config::Config::get_current_dir()?;
41        match LockfileResult::find_in_directory(cwd) {
42            LockfileResult::Lockfile(lockfile) => {
43                let has_modules = !lockfile.modules.is_empty();
44                let has_commands = !lockfile.commands.is_empty();
45                // unset local if there's nothing to list
46                local = has_modules | has_commands;
47                if has_modules {
48                    writeln!(handle, "LOCAL PACKAGES:")?;
49                    write!(handle, "{}", create_module_ascii_table(&lockfile.modules))?;
50                }
51                if has_modules && has_commands {
52                    writeln!(handle)?;
53                }
54                if has_commands {
55                    writeln!(handle, "LOCAL COMMANDS:")?;
56                    write!(handle, "{}", create_command_ascii_table(&lockfile.commands))?;
57                }
58            }
59            LockfileResult::NoLockfile => {
60                if !global {
61                    writeln!(handle, "No packages in current directory")?;
62                    return Ok(());
63                }
64                local = false;
65            }
66            LockfileResult::LockfileError(e) => {
67                return Err(anyhow!(
68                    "Failed to read lock file in current directory: {}",
69                    e
70                ));
71            }
72        }
73    }
74
75    if local && global {
76        writeln!(handle)?;
77    }
78
79    if global {
80        let global_path = config::Config::get_globals_directory()?;
81        match LockfileResult::find_in_directory(global_path) {
82            LockfileResult::Lockfile(lockfile) => {
83                let has_modules = !lockfile.modules.is_empty();
84                let has_commands = !lockfile.commands.is_empty();
85                // unset global if there's nothing to list
86                global = has_modules || has_commands;
87                if has_modules {
88                    writeln!(handle, "GLOBAL PACKAGES:")?;
89                    write!(handle, "{}", create_module_ascii_table(&lockfile.modules))?;
90                }
91                if has_modules && has_commands {
92                    writeln!(handle)?;
93                }
94                if has_commands {
95                    writeln!(handle, "GLOBAL COMMANDS:")?;
96                    write!(handle, "{}", create_command_ascii_table(&lockfile.commands))?;
97                }
98            }
99            LockfileResult::NoLockfile => {
100                if !local_start_value {
101                    writeln!(handle, "No global packages")?;
102                    return Ok(());
103                }
104                global = false;
105            }
106            LockfileResult::LockfileError(e) => {
107                return Err(anyhow!(
108                    "Failed to read lock file in current directory: {}",
109                    e
110                ));
111            }
112        }
113    }
114
115    if handle.is_empty() {
116        // we should only have an empty string if we unset both global and local
117        debug_assert!(!global);
118        debug_assert!(!local);
119        println!("No packages found");
120    } else {
121        print!("{}", handle);
122    }
123    Ok(())
124}
125
126fn create_module_ascii_table(modules: &ModuleMap) -> String {
127    let mut table = Table::new();
128    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
129    table.add_row(row!["PACKAGE", "VERSION", "MODULE", "ABI"]);
130    for (package_name, version_info) in modules.iter() {
131        for (version_number, module_info) in version_info.iter() {
132            for (module_name, module) in module_info.iter() {
133                table.add_row(row![package_name, version_number, module_name, module.abi,]);
134            }
135        }
136    }
137    format!("{}", table)
138}
139
140fn create_command_ascii_table(commands: &CommandMap) -> String {
141    let mut table = Table::new();
142    table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
143    table.add_row(row!["COMMAND", "PACKAGE", "VERSION"]);
144    for (command_name, command) in commands.iter() {
145        table.add_row(row![
146            command_name,
147            command.package_name,
148            command.package_version,
149        ]);
150    }
151    format!("{}", table)
152}