Skip to main content

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
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 from
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    /// Perform all checks without installing (unstable)
67    #[arg(short = 'n', long)]
68    #[cfg_attr(feature = "serde", serde(default))]
69    pub dry_run: bool,
70
71    /// Do not save tracking information
72    #[arg(long)]
73    #[cfg_attr(feature = "serde", serde(default))]
74    pub no_track: bool,
75
76    /// Build in debug mode (with the 'dev' profile) instead of release mode
77    #[arg(long, conflicts_with = "profile")]
78    #[cfg_attr(feature = "serde", serde(default))]
79    pub debug: bool,
80
81    /// Directory to install packages into
82    #[arg(long, value_name = "DIR")]
83    #[cfg_attr(feature = "serde", serde(default))]
84    pub root: Option<PathBuf>,
85
86    /// Registry index to install from
87    #[arg(
88        long,
89        value_name = "INDEX",
90        conflicts_with_all = ["git", "path", "registry"],
91        requires = "crates",
92    )]
93    #[cfg_attr(feature = "serde", serde(default))]
94    pub index: Option<String>,
95
96    /// Registry to use
97    #[arg(
98        long,
99        value_name = "REGISTRY",
100        conflicts_with_all = ["git", "path", "index"],
101        requires = "crates",
102    )]
103    #[cfg_attr(feature = "serde", serde(default))]
104    pub registry: Option<String>,
105
106    /// Ignore `rust-version` specification in packages
107    #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
108    #[cfg_attr(feature = "serde", serde(default))]
109    pub ignore_rust_version: bool,
110
111    /// Install only the specified binary
112    #[arg(
113        long,
114        value_name = "NAME",
115        action = ArgAction::Append,
116        num_args=0..=1,
117        help_heading = heading::TARGET_SELECTION,
118    )]
119    #[cfg_attr(feature = "serde", serde(default))]
120    pub bin: Vec<String>,
121
122    /// Install all binaries
123    #[arg(long, help_heading = heading::TARGET_SELECTION)]
124    #[cfg_attr(feature = "serde", serde(default))]
125    pub bins: bool,
126
127    /// Install only the specified example
128    #[arg(
129        long,
130        value_name = "NAME",
131        action = ArgAction::Append,
132        num_args=0..=1,
133        help_heading = heading::TARGET_SELECTION,
134    )]
135    #[cfg_attr(feature = "serde", serde(default))]
136    pub example: Vec<String>,
137
138    /// Install all examples
139    #[arg(long, help_heading = heading::TARGET_SELECTION)]
140    #[cfg_attr(feature = "serde", serde(default))]
141    pub examples: bool,
142
143    /// Select the package from the given source
144    #[arg(value_name = "CRATE[@<VER>]", action = ArgAction::Append, num_args = 0..)]
145    #[cfg_attr(feature = "serde", serde(default))]
146    pub crates: Vec<String>,
147}
148
149impl Install {
150    /// Build a `cargo install` command
151    pub fn command(&self) -> Command {
152        let mut cmd = CommonOptions::cargo_command();
153        cmd.arg("install");
154
155        self.common.apply(&mut cmd);
156
157        if let Some(version) = self.version.as_ref() {
158            cmd.arg("--version").arg(version);
159        }
160        if let Some(git) = self.git.as_ref() {
161            cmd.arg("--git").arg(git);
162        }
163        if let Some(branch) = self.branch.as_ref() {
164            cmd.arg("--branch").arg(branch);
165        }
166        if let Some(tag) = self.tag.as_ref() {
167            cmd.arg("--tag").arg(tag);
168        }
169        if let Some(rev) = self.rev.as_ref() {
170            cmd.arg("--rev").arg(rev);
171        }
172        if let Some(path) = self.path.as_ref() {
173            cmd.arg("--path").arg(path);
174        }
175        if self.list {
176            cmd.arg("--list");
177        }
178        if self.force {
179            cmd.arg("--force");
180        }
181        if self.dry_run {
182            cmd.arg("--dry-run");
183        }
184        if self.no_track {
185            cmd.arg("--no-track");
186        }
187        if self.debug {
188            cmd.arg("--debug");
189        }
190        if let Some(root) = self.root.as_ref() {
191            cmd.arg("--root").arg(root);
192        }
193        if let Some(index) = self.index.as_ref() {
194            cmd.arg("--index").arg(index);
195        }
196        if let Some(registry) = self.registry.as_ref() {
197            cmd.arg("--registry").arg(registry);
198        }
199        if self.ignore_rust_version {
200            cmd.arg("--ignore-rust-version");
201        }
202        for bin in &self.bin {
203            cmd.arg("--bin").arg(bin);
204        }
205        if self.bins {
206            cmd.arg("--bins");
207        }
208        for example in &self.example {
209            cmd.arg("--example").arg(example);
210        }
211        if self.examples {
212            cmd.arg("--examples");
213        }
214        cmd.args(&self.crates);
215
216        cmd
217    }
218}
219
220impl Deref for Install {
221    type Target = CommonOptions;
222
223    fn deref(&self) -> &Self::Target {
224        &self.common
225    }
226}
227
228impl DerefMut for Install {
229    fn deref_mut(&mut self) -> &mut Self::Target {
230        &mut self.common
231    }
232}
233
234#[cfg(test)]
235mod test {
236    use super::Install;
237    use clap::CommandFactory;
238
239    #[test]
240    fn verify_cli() {
241        <Install as CommandFactory>::command().debug_assert()
242    }
243}