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#[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 #[arg(long, value_name = "VERSION", alias = "vers", requires = "crates")]
28 #[cfg_attr(feature = "serde", serde(default))]
29 pub version: Option<String>,
30
31 #[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 #[arg(long, value_name = "BRANCH", requires = "git")]
38 #[cfg_attr(feature = "serde", serde(default))]
39 pub branch: Option<String>,
40
41 #[arg(long, value_name = "TAG", requires = "git")]
43 #[cfg_attr(feature = "serde", serde(default))]
44 pub tag: Option<String>,
45
46 #[arg(long, value_name = "SHA", requires = "git")]
48 #[cfg_attr(feature = "serde", serde(default))]
49 pub rev: Option<String>,
50
51 #[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 #[arg(long)]
58 #[cfg_attr(feature = "serde", serde(default))]
59 pub list: bool,
60
61 #[arg(short, long)]
63 #[cfg_attr(feature = "serde", serde(default))]
64 pub force: bool,
65
66 #[arg(short = 'n', long)]
68 #[cfg_attr(feature = "serde", serde(default))]
69 pub dry_run: bool,
70
71 #[arg(long)]
73 #[cfg_attr(feature = "serde", serde(default))]
74 pub no_track: bool,
75
76 #[arg(long, conflicts_with = "profile")]
78 #[cfg_attr(feature = "serde", serde(default))]
79 pub debug: bool,
80
81 #[arg(long, value_name = "DIR")]
83 #[cfg_attr(feature = "serde", serde(default))]
84 pub root: Option<PathBuf>,
85
86 #[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 #[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 #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
108 #[cfg_attr(feature = "serde", serde(default))]
109 pub ignore_rust_version: bool,
110
111 #[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 #[arg(long, help_heading = heading::TARGET_SELECTION)]
124 #[cfg_attr(feature = "serde", serde(default))]
125 pub bins: bool,
126
127 #[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 #[arg(long, help_heading = heading::TARGET_SELECTION)]
140 #[cfg_attr(feature = "serde", serde(default))]
141 pub examples: bool,
142
143 #[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 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}