cargo_options/
install.rs

1use std::ops::{Deref, DerefMut};
2use std::path::PathBuf;
3use std::process::Command;
4
5use clap::{ArgAction, Parser};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10use crate::common::CommonOptions;
11use crate::heading;
12
13/// Install a Rust binary. Default location is $HOME/.cargo/bin
14#[derive(Clone, Debug, Default, Parser)]
15#[command(
16    display_order = 1,
17    after_help = "Run `cargo help install` for more detailed information."
18)]
19#[group(skip)]
20#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
21pub struct Install {
22    #[command(flatten)]
23    #[cfg_attr(feature = "serde", serde(flatten))]
24    pub common: CommonOptions,
25
26    /// Specify a version to install
27    #[arg(long, value_name = "VERSION", alias = "vers", requires = "crates")]
28    #[cfg_attr(feature = "serde", serde(default))]
29    pub version: Option<String>,
30
31    /// Git URL to install the specified crate from
32    #[arg(long, value_name = "URL", conflicts_with_all = ["path", "index", "registry"])]
33    #[cfg_attr(feature = "serde", serde(default))]
34    pub git: Option<String>,
35
36    /// Branch to use when installing from git
37    #[arg(long, value_name = "BRANCH", requires = "git")]
38    #[cfg_attr(feature = "serde", serde(default))]
39    pub branch: Option<String>,
40
41    /// Tag to use when installing from git
42    #[arg(long, value_name = "TAG", requires = "git")]
43    #[cfg_attr(feature = "serde", serde(default))]
44    pub tag: Option<String>,
45
46    /// Specific commit to use when installing from git
47    #[arg(long, value_name = "SHA", requires = "git")]
48    #[cfg_attr(feature = "serde", serde(default))]
49    pub rev: Option<String>,
50
51    /// Filesystem path to local crate to install
52    #[arg(long, value_name = "PATH", conflicts_with_all = ["git", "index", "registry"])]
53    #[cfg_attr(feature = "serde", serde(default))]
54    pub path: Option<PathBuf>,
55
56    /// list all installed packages and their versions
57    #[arg(long)]
58    #[cfg_attr(feature = "serde", serde(default))]
59    pub list: bool,
60
61    /// Force overwriting existing crates or binaries
62    #[arg(short, long)]
63    #[cfg_attr(feature = "serde", serde(default))]
64    pub force: bool,
65
66    /// Do not save tracking information
67    #[arg(long)]
68    #[cfg_attr(feature = "serde", serde(default))]
69    pub no_track: bool,
70
71    /// Build in debug mode (with the 'dev' profile) instead of release mode
72    #[arg(long)]
73    #[cfg_attr(feature = "serde", serde(default))]
74    pub debug: bool,
75
76    /// Directory to install packages into
77    #[arg(long, value_name = "DIR")]
78    #[cfg_attr(feature = "serde", serde(default))]
79    pub root: Option<PathBuf>,
80
81    /// Registry index to install from
82    #[arg(
83        long,
84        value_name = "INDEX",
85        conflicts_with_all = ["git", "path", "registry"],
86        requires = "crates",
87    )]
88    #[cfg_attr(feature = "serde", serde(default))]
89    pub index: Option<String>,
90
91    /// Registry to use
92    #[arg(
93        long,
94        value_name = "REGISTRY",
95        conflicts_with_all = ["git", "path", "index"],
96        requires = "crates",
97    )]
98    #[cfg_attr(feature = "serde", serde(default))]
99    pub registry: Option<String>,
100
101    /// Install only the specified binary
102    #[arg(
103        long,
104        value_name = "NAME",
105        action = ArgAction::Append,
106        num_args=0..=1,
107        help_heading = heading::TARGET_SELECTION,
108    )]
109    #[cfg_attr(feature = "serde", serde(default))]
110    pub bin: Vec<String>,
111
112    /// Install all binaries
113    #[arg(long, help_heading = heading::TARGET_SELECTION)]
114    #[cfg_attr(feature = "serde", serde(default))]
115    pub bins: bool,
116
117    /// Install only the specified example
118    #[arg(
119        long,
120        value_name = "NAME",
121        action = ArgAction::Append,
122        num_args=0..=1,
123        help_heading = heading::TARGET_SELECTION,
124    )]
125    #[cfg_attr(feature = "serde", serde(default))]
126    pub example: Vec<String>,
127
128    /// Install all examples
129    #[arg(long, help_heading = heading::TARGET_SELECTION)]
130    #[cfg_attr(feature = "serde", serde(default))]
131    pub examples: bool,
132
133    #[arg(value_name = "crate", action = ArgAction::Append, num_args = 0..)]
134    #[cfg_attr(feature = "serde", serde(default))]
135    pub crates: Vec<String>,
136}
137
138impl Install {
139    /// Build a `cargo install` command
140    pub fn command(&self) -> Command {
141        let mut cmd = CommonOptions::cargo_command();
142        cmd.arg("install");
143
144        self.common.apply(&mut cmd);
145
146        if let Some(version) = self.version.as_ref() {
147            cmd.arg("--version").arg(version);
148        }
149        if let Some(git) = self.git.as_ref() {
150            cmd.arg("--git").arg(git);
151        }
152        if let Some(branch) = self.branch.as_ref() {
153            cmd.arg("--branch").arg(branch);
154        }
155        if let Some(tag) = self.tag.as_ref() {
156            cmd.arg("--tag").arg(tag);
157        }
158        if let Some(rev) = self.rev.as_ref() {
159            cmd.arg("--rev").arg(rev);
160        }
161        if let Some(path) = self.path.as_ref() {
162            cmd.arg("--path").arg(path);
163        }
164        if self.list {
165            cmd.arg("--list");
166        }
167        if self.force {
168            cmd.arg("--force");
169        }
170        if self.no_track {
171            cmd.arg("--no-track");
172        }
173        if self.debug {
174            cmd.arg("--debug");
175        }
176        if let Some(root) = self.root.as_ref() {
177            cmd.arg("--root").arg(root);
178        }
179        if let Some(index) = self.index.as_ref() {
180            cmd.arg("--index").arg(index);
181        }
182        if let Some(registry) = self.registry.as_ref() {
183            cmd.arg("--registry").arg(registry);
184        }
185        for bin in &self.bin {
186            cmd.arg("--bin").arg(bin);
187        }
188        if self.bins {
189            cmd.arg("--bins");
190        }
191        for example in &self.example {
192            cmd.arg("--example").arg(example);
193        }
194        if self.examples {
195            cmd.arg("--examples");
196        }
197        cmd.args(&self.crates);
198
199        cmd
200    }
201}
202
203impl Deref for Install {
204    type Target = CommonOptions;
205
206    fn deref(&self) -> &Self::Target {
207        &self.common
208    }
209}
210
211impl DerefMut for Install {
212    fn deref_mut(&mut self) -> &mut Self::Target {
213        &mut self.common
214    }
215}
216
217#[cfg(test)]
218mod test {
219    use super::Install;
220    use clap::CommandFactory;
221
222    #[test]
223    fn verify_cli() {
224        <Install as CommandFactory>::command().debug_assert()
225    }
226}