1use std::{
2 ffi::OsStr,
3 path::PathBuf,
4 process::{Child, Stdio},
5};
6
7use super::SubCommand;
8
9use crate::{global::GlobalOpts, spawn::ParameterizedSpawn};
10
11#[derive(Debug, Clone, Default)]
15pub struct Add {
16 bin: PathBuf,
17
18 global_opts: GlobalOpts,
19
20 change_list: Option<String>,
21
22 downgrade: bool,
23
24 force_literal_filenames: bool,
25
26 skip_ignore: bool,
27
28 preview: bool,
29
30 filetype: Option<String>,
31}
32
33impl SubCommand for Add {
34 fn name(&self) -> &str {
35 "add"
36 }
37
38 fn inject_local_args(&self, command: &mut std::process::Command) {
39 if let Some(change_list) = self.change_list.as_ref() {
40 command.arg("-c").arg(change_list);
41 }
42 if self.downgrade {
43 command.arg("-d");
44 }
45 if self.force_literal_filenames {
46 command.arg("-f");
47 }
48 if self.skip_ignore {
49 command.arg("-I");
50 }
51 if self.preview {
52 command.arg("-n");
53 }
54 if let Some(filetype) = self.filetype.as_ref() {
55 command.arg("-t").arg(filetype);
56 }
57 }
58
59 fn global_opts(&self) -> Option<&GlobalOpts> {
60 Some(&self.global_opts)
61 }
62}
63
64impl ParameterizedSpawn for Add {
65 type Input<'a> = &'a [&'a OsStr];
66 type Output<'a> = Child;
67 type Error = std::io::Error;
68
69 fn spawn_with<'a>(&mut self, input: Self::Input<'a>) -> Result<Self::Output<'a>, Self::Error> {
75 self.setup_command(&self.bin)
76 .args(input)
77 .stdout(Stdio::piped())
78 .stderr(Stdio::piped())
79 .spawn()
80 }
81}
82
83impl Add {
84 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
88 Self {
89 bin: bin.into(),
90 global_opts,
91 ..Default::default()
92 }
93 }
94
95 #[cfg_attr(
100 feature = "lt2014_2",
101 doc = "See the [Global Options](GlobalOpts) section."
102 )]
103 #[cfg_attr(
104 all(feature = "lt2015_1", not(feature = "lt2014_2")),
105 doc = "See the [“Global Options”](GlobalOpts) section."
106 )]
107 #[cfg_attr(
108 all(feature = "lt2017_1", not(feature = "lt2015_1")),
109 doc = "See [“Global Options”](GlobalOpts)."
110 )]
111 #[cfg_attr(
112 all(feature = "lt2018_2", not(feature = "lt2017_1")),
113 doc = "See [Global Options](GlobalOpts)."
114 )]
115 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
116 pub fn get_global_opts(&self) -> &GlobalOpts {
117 &self.global_opts
118 }
119
120 #[cfg_attr(
125 feature = "lt2014_2",
126 doc = "See the [Global Options](GlobalOpts) section."
127 )]
128 #[cfg_attr(
129 all(feature = "lt2015_1", not(feature = "lt2014_2")),
130 doc = "See the [“Global Options”](GlobalOpts) section."
131 )]
132 #[cfg_attr(
133 all(feature = "lt2017_1", not(feature = "lt2015_1")),
134 doc = "See [“Global Options”](GlobalOpts)."
135 )]
136 #[cfg_attr(
137 all(feature = "lt2018_2", not(feature = "lt2017_1")),
138 doc = "See [Global Options](GlobalOpts)."
139 )]
140 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
141 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
142 self.global_opts = v;
143 self
144 }
145
146 #[cfg_attr(
151 feature = "lt2014_2",
152 doc = "See the [Global Options](GlobalOpts) section."
153 )]
154 #[cfg_attr(
155 all(feature = "lt2015_1", not(feature = "lt2014_2")),
156 doc = "See the [“Global Options”](GlobalOpts) section."
157 )]
158 #[cfg_attr(
159 all(feature = "lt2017_1", not(feature = "lt2015_1")),
160 doc = "See [“Global Options”](GlobalOpts)."
161 )]
162 #[cfg_attr(
163 all(feature = "lt2018_2", not(feature = "lt2017_1")),
164 doc = "See [Global Options](GlobalOpts)."
165 )]
166 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
167 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
168 self.global_opts = v;
169 self
170 }
171
172 #[cfg_attr(feature = "lt2014_2", doc = "flag is")]
178 #[cfg_attr(not(feature = "lt2014_2"), doc = "option is")]
179 pub fn get_change_list(&self) -> Option<&String> {
181 self.change_list.as_ref()
182 }
183
184 #[cfg_attr(feature = "lt2014_2", doc = "flag is")]
190 #[cfg_attr(not(feature = "lt2014_2"), doc = "option is")]
191 pub fn set_change_list(&mut self, v: impl Into<String>) -> &mut Self {
193 self.change_list = Some(v.into());
194 self
195 }
196
197 #[cfg_attr(feature = "lt2014_2", doc = "flag is")]
203 #[cfg_attr(not(feature = "lt2014_2"), doc = "option is")]
204 pub fn change_list(mut self, v: impl Into<String>) -> Self {
206 self.change_list = Some(v.into());
207 self
208 }
209
210 pub fn get_downgrade(&self) -> bool {
216 self.downgrade
217 }
218 pub fn set_downgrade(&mut self, v: bool) -> &mut Self {
224 self.downgrade = v;
225 self
226 }
227 pub fn downgrade(mut self, v: bool) -> Self {
233 self.downgrade = v;
234 self
235 }
236
237 #[cfg_attr(
243 feature = "lt2014_2",
244 doc = "flag to force inclusion of wildcards in filenames. See the",
245 doc = "File Specifications chapter for details."
246 )]
247 #[cfg_attr(
248 all(feature = "lt2015_1", not(feature = "lt2014_2")),
249 doc = "option to force inclusion of wildcards in filenames. See the",
250 doc = "“File Specifications” chapter for details."
251 )]
252 #[cfg_attr(
253 all(feature = "lt2017_1", not(feature = "lt2015_1")),
254 doc = "option to force inclusion of wildcards in filenames. See",
255 doc = "“File Specifications” for details."
256 )]
257 #[cfg_attr(
258 all(feature = "lt2018_2", not(feature = "lt2017_1")),
259 doc = "option to force inclusion of wildcards in filenames. See File",
260 doc = "Specifications for details."
261 )]
262 #[cfg_attr(
263 all(feature = "lt2025_1", not(feature = "lt2018_2")),
264 doc = "option to force inclusion of wildcards in filenames. See File",
265 doc = "specifications for details."
266 )]
267 #[cfg_attr(
268 not(feature = "lt2025_1"),
269 doc = "option to force inclusion of wildcards in filenames. See File",
270 doc = "specifications for details.",
271 doc = "",
272 doc = "Filenames that contain the special characters '@', '#', '%', or '*' are",
273 doc = "reformatted to encode the characters using ASCII hexadecimal",
274 doc = "representation. After the files are added, refer to them using the",
275 doc = "reformatted file name instead of the local file system name."
276 )]
277 pub fn get_force_literal_filenames(&self) -> bool {
278 self.force_literal_filenames
279 }
280
281 #[cfg_attr(
287 feature = "lt2014_2",
288 doc = "flag to force inclusion of wildcards in filenames. See the",
289 doc = "File Specifications chapter for details."
290 )]
291 #[cfg_attr(
292 all(feature = "lt2015_1", not(feature = "lt2014_2")),
293 doc = "option to force inclusion of wildcards in filenames. See the",
294 doc = "“File Specifications” chapter for details."
295 )]
296 #[cfg_attr(
297 all(feature = "lt2017_1", not(feature = "lt2015_1")),
298 doc = "option to force inclusion of wildcards in filenames. See",
299 doc = "“File Specifications” for details."
300 )]
301 #[cfg_attr(
302 all(feature = "lt2018_2", not(feature = "lt2017_1")),
303 doc = "option to force inclusion of wildcards in filenames. See File",
304 doc = "Specifications for details."
305 )]
306 #[cfg_attr(
307 all(feature = "lt2025_1", not(feature = "lt2018_2")),
308 doc = "option to force inclusion of wildcards in filenames. See File",
309 doc = "specifications for details."
310 )]
311 #[cfg_attr(
312 not(feature = "lt2025_1"),
313 doc = "option to force inclusion of wildcards in filenames. See File",
314 doc = "specifications for details.",
315 doc = "",
316 doc = "Filenames that contain the special characters '@', '#', '%', or '*' are",
317 doc = "reformatted to encode the characters using ASCII hexadecimal",
318 doc = "representation. After the files are added, refer to them using the",
319 doc = "reformatted file name instead of the local file system name."
320 )]
321 pub fn set_force_literal_filenames(&mut self, v: bool) -> &mut Self {
322 self.force_literal_filenames = v;
323 self
324 }
325
326 #[cfg_attr(
332 feature = "lt2014_2",
333 doc = "flag to force inclusion of wildcards in filenames. See the",
334 doc = "File Specifications chapter for details."
335 )]
336 #[cfg_attr(
337 all(feature = "lt2015_1", not(feature = "lt2014_2")),
338 doc = "option to force inclusion of wildcards in filenames. See the",
339 doc = "“File Specifications” chapter for details."
340 )]
341 #[cfg_attr(
342 all(feature = "lt2017_1", not(feature = "lt2015_1")),
343 doc = "option to force inclusion of wildcards in filenames. See",
344 doc = "“File Specifications” for details."
345 )]
346 #[cfg_attr(
347 all(feature = "lt2018_2", not(feature = "lt2017_1")),
348 doc = "option to force inclusion of wildcards in filenames. See File",
349 doc = "Specifications for details."
350 )]
351 #[cfg_attr(
352 all(feature = "lt2025_1", not(feature = "lt2018_2")),
353 doc = "option to force inclusion of wildcards in filenames. See File",
354 doc = "specifications for details."
355 )]
356 #[cfg_attr(
357 not(feature = "lt2025_1"),
358 doc = "option to force inclusion of wildcards in filenames. See File",
359 doc = "specifications for details.",
360 doc = "",
361 doc = "Filenames that contain the special characters '@', '#', '%', or '*' are",
362 doc = "reformatted to encode the characters using ASCII hexadecimal",
363 doc = "representation. After the files are added, refer to them using the",
364 doc = "reformatted file name instead of the local file system name."
365 )]
366 pub fn force_literal_filenames(mut self, v: bool) -> Self {
367 self.force_literal_filenames = v;
368 self
369 }
370
371 pub fn get_skip_ignore(&self) -> bool {
378 self.skip_ignore
379 }
380 pub fn set_skip_ignore(&mut self, v: bool) -> &mut Self {
387 self.skip_ignore = v;
388 self
389 }
390 pub fn skip_ignore(mut self, v: bool) -> Self {
397 self.skip_ignore = v;
398 self
399 }
400
401 pub fn get_preview(&self) -> bool {
408 self.preview
409 }
410 pub fn set_preview(&mut self, v: bool) -> &mut Self {
417 self.preview = v;
418 self
419 }
420 pub fn preview(mut self, v: bool) -> Self {
427 self.preview = v;
428 self
429 }
430
431 #[cfg_attr(
438 feature = "lt2014_2",
439 doc = "Please see the File Types chapter for a list of Perforce",
440 doc = "file types."
441 )]
442 #[cfg_attr(
443 all(feature = "lt2015_1", not(feature = "lt2014_2")),
444 doc = "Please see the “File Types” chapter for a list of",
445 doc = "Perforce file types."
446 )]
447 #[cfg_attr(
448 all(feature = "lt2017_1", not(feature = "lt2015_1")),
449 doc = "See “File Types” for a list of Perforce file types."
450 )]
451 #[cfg_attr(
452 all(feature = "lt2017_2", not(feature = "lt2017_1")),
453 doc = "See File Types for a list of Perforce file types."
454 )]
455 #[cfg_attr(
456 all(feature = "lt2018_2", not(feature = "lt2017_2")),
457 doc = "See File Types for a list of Helix Server file types."
458 )]
459 #[cfg_attr(
460 all(feature = "lt2019_1", not(feature = "lt2018_2")),
461 doc = "See File types for a list of Helix Server file types."
462 )]
463 #[cfg_attr(
464 all(feature = "lt2021_1", not(feature = "lt2019_1")),
465 doc = "See File types for a list of Helix server file types."
466 )]
467 #[cfg_attr(
468 all(feature = "lt2024_1", not(feature = "lt2021_1")),
469 doc = "See File types for a list of Helix Server file types."
470 )]
471 #[cfg_attr(
472 all(feature = "lt2025_2", not(feature = "lt2024_1")),
473 doc = "See File types as well as the lbr.autocompress",
474 doc = "configurable."
475 )]
476 #[cfg_attr(
477 not(feature = "lt2025_2"),
478 doc = "See Base file types and modifiers as well as the",
479 doc = "lbr.autocompress configurable."
480 )]
481 pub fn get_filetype(&self) -> Option<&String> {
482 self.filetype.as_ref()
483 }
484
485 #[cfg_attr(
492 feature = "lt2014_2",
493 doc = "Please see the File Types chapter for a list of Perforce",
494 doc = "file types."
495 )]
496 #[cfg_attr(
497 all(feature = "lt2015_1", not(feature = "lt2014_2")),
498 doc = "Please see the “File Types” chapter for a list of",
499 doc = "Perforce file types."
500 )]
501 #[cfg_attr(
502 all(feature = "lt2017_1", not(feature = "lt2015_1")),
503 doc = "See “File Types” for a list of Perforce file types."
504 )]
505 #[cfg_attr(
506 all(feature = "lt2017_2", not(feature = "lt2017_1")),
507 doc = "See File Types for a list of Perforce file types."
508 )]
509 #[cfg_attr(
510 all(feature = "lt2018_2", not(feature = "lt2017_2")),
511 doc = "See File Types for a list of Helix Server file types."
512 )]
513 #[cfg_attr(
514 all(feature = "lt2019_1", not(feature = "lt2018_2")),
515 doc = "See File types for a list of Helix Server file types."
516 )]
517 #[cfg_attr(
518 all(feature = "lt2021_1", not(feature = "lt2019_1")),
519 doc = "See File types for a list of Helix server file types."
520 )]
521 #[cfg_attr(
522 all(feature = "lt2024_1", not(feature = "lt2021_1")),
523 doc = "See File types for a list of Helix Server file types."
524 )]
525 #[cfg_attr(
526 all(feature = "lt2025_2", not(feature = "lt2024_1")),
527 doc = "See File types as well as the lbr.autocompress",
528 doc = "configurable."
529 )]
530 #[cfg_attr(
531 not(feature = "lt2025_2"),
532 doc = "See Base file types and modifiers as well as the",
533 doc = "lbr.autocompress configurable."
534 )]
535 pub fn set_filetype(&mut self, v: impl Into<String>) -> &mut Self {
536 self.filetype = Some(v.into());
537 self
538 }
539
540 #[cfg_attr(
547 feature = "lt2014_2",
548 doc = "Please see the File Types chapter for a list of Perforce",
549 doc = "file types."
550 )]
551 #[cfg_attr(
552 all(feature = "lt2015_1", not(feature = "lt2014_2")),
553 doc = "Please see the “File Types” chapter for a list of",
554 doc = "Perforce file types."
555 )]
556 #[cfg_attr(
557 all(feature = "lt2017_1", not(feature = "lt2015_1")),
558 doc = "See “File Types” for a list of Perforce file types."
559 )]
560 #[cfg_attr(
561 all(feature = "lt2017_2", not(feature = "lt2017_1")),
562 doc = "See File Types for a list of Perforce file types."
563 )]
564 #[cfg_attr(
565 all(feature = "lt2018_2", not(feature = "lt2017_2")),
566 doc = "See File Types for a list of Helix Server file types."
567 )]
568 #[cfg_attr(
569 all(feature = "lt2019_1", not(feature = "lt2018_2")),
570 doc = "See File types for a list of Helix Server file types."
571 )]
572 #[cfg_attr(
573 all(feature = "lt2021_1", not(feature = "lt2019_1")),
574 doc = "See File types for a list of Helix server file types."
575 )]
576 #[cfg_attr(
577 all(feature = "lt2024_1", not(feature = "lt2021_1")),
578 doc = "See File types for a list of Helix Server file types."
579 )]
580 #[cfg_attr(
581 all(feature = "lt2025_2", not(feature = "lt2024_1")),
582 doc = "See File types as well as the lbr.autocompress",
583 doc = "configurable."
584 )]
585 #[cfg_attr(
586 not(feature = "lt2025_2"),
587 doc = "See Base file types and modifiers as well as the",
588 doc = "lbr.autocompress configurable."
589 )]
590 pub fn filetype(mut self, v: impl Into<String>) -> Self {
591 self.filetype = Some(v.into());
592 self
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use super::*;
599 use crate::cmd::args_of;
600
601 #[test]
604 fn without_options() {
605 let add = Add::new("p4", GlobalOpts::new());
606
607 let command = add.setup_command("p4");
608
609 assert_eq!(command.get_program(), OsStr::new("p4"));
610 assert_eq!(args_of(&command), ["add"]);
611 }
612
613 #[test]
614 fn all_local_options() {
615 let mut add = Add::new("p4", GlobalOpts::new());
616 add.set_change_list("42")
617 .set_downgrade(true)
618 .set_force_literal_filenames(true)
619 .set_skip_ignore(true)
620 .set_preview(true)
621 .set_filetype("text");
622
623 assert_eq!(
624 args_of(&add.setup_command("p4")),
625 ["add", "-c", "42", "-d", "-f", "-I", "-n", "-t", "text"]
626 );
627 }
628}