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(long)]
68 #[cfg_attr(feature = "serde", serde(default))]
69 pub no_track: bool,
70
71 #[arg(long)]
73 #[cfg_attr(feature = "serde", serde(default))]
74 pub debug: bool,
75
76 #[arg(long, value_name = "DIR")]
78 #[cfg_attr(feature = "serde", serde(default))]
79 pub root: Option<PathBuf>,
80
81 #[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 #[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 #[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 #[arg(long, help_heading = heading::TARGET_SELECTION)]
114 #[cfg_attr(feature = "serde", serde(default))]
115 pub bins: bool,
116
117 #[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 #[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 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}