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<S, I> ParameterizedSpawn<(S,)> for Add
65where
66 S: IntoIterator<Item = I>,
67 I: AsRef<OsStr>,
68{
69 type Output = Child;
70 type Error = std::io::Error;
71
72 fn spawn_with(&mut self, (input,): (S,)) -> Result<Self::Output, Self::Error> {
78 self.setup_command(&self.bin)
79 .args(input)
80 .stdout(Stdio::piped())
81 .stderr(Stdio::piped())
82 .spawn()
83 }
84}
85
86impl Add {
87 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
91 Self {
92 bin: bin.into(),
93 global_opts,
94 ..Default::default()
95 }
96 }
97
98 #[cfg_attr(
103 feature = "lt2014_2",
104 doc = "See the [Global Options](GlobalOpts) section."
105 )]
106 #[cfg_attr(
107 all(feature = "lt2015_1", not(feature = "lt2014_2")),
108 doc = "See the [“Global Options”](GlobalOpts) section."
109 )]
110 #[cfg_attr(
111 all(feature = "lt2017_1", not(feature = "lt2015_1")),
112 doc = "See [“Global Options”](GlobalOpts)."
113 )]
114 #[cfg_attr(
115 all(feature = "lt2018_2", not(feature = "lt2017_1")),
116 doc = "See [Global Options](GlobalOpts)."
117 )]
118 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
119 pub fn get_global_opts(&self) -> &GlobalOpts {
120 &self.global_opts
121 }
122
123 #[cfg_attr(
128 feature = "lt2014_2",
129 doc = "See the [Global Options](GlobalOpts) section."
130 )]
131 #[cfg_attr(
132 all(feature = "lt2015_1", not(feature = "lt2014_2")),
133 doc = "See the [“Global Options”](GlobalOpts) section."
134 )]
135 #[cfg_attr(
136 all(feature = "lt2017_1", not(feature = "lt2015_1")),
137 doc = "See [“Global Options”](GlobalOpts)."
138 )]
139 #[cfg_attr(
140 all(feature = "lt2018_2", not(feature = "lt2017_1")),
141 doc = "See [Global Options](GlobalOpts)."
142 )]
143 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
144 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
145 self.global_opts = v;
146 self
147 }
148
149 #[cfg_attr(
154 feature = "lt2014_2",
155 doc = "See the [Global Options](GlobalOpts) section."
156 )]
157 #[cfg_attr(
158 all(feature = "lt2015_1", not(feature = "lt2014_2")),
159 doc = "See the [“Global Options”](GlobalOpts) section."
160 )]
161 #[cfg_attr(
162 all(feature = "lt2017_1", not(feature = "lt2015_1")),
163 doc = "See [“Global Options”](GlobalOpts)."
164 )]
165 #[cfg_attr(
166 all(feature = "lt2018_2", not(feature = "lt2017_1")),
167 doc = "See [Global Options](GlobalOpts)."
168 )]
169 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
170 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
171 self.global_opts = v;
172 self
173 }
174
175 #[cfg_attr(feature = "lt2014_2", doc = "flag is")]
181 #[cfg_attr(not(feature = "lt2014_2"), doc = "option is")]
182 pub fn get_change_list(&self) -> Option<&String> {
184 self.change_list.as_ref()
185 }
186
187 #[cfg_attr(feature = "lt2014_2", doc = "flag is")]
193 #[cfg_attr(not(feature = "lt2014_2"), doc = "option is")]
194 pub fn set_change_list(&mut self, v: impl Into<String>) -> &mut Self {
196 self.change_list = Some(v.into());
197 self
198 }
199
200 #[cfg_attr(feature = "lt2014_2", doc = "flag is")]
206 #[cfg_attr(not(feature = "lt2014_2"), doc = "option is")]
207 pub fn change_list(mut self, v: impl Into<String>) -> Self {
209 self.change_list = Some(v.into());
210 self
211 }
212
213 pub fn get_downgrade(&self) -> bool {
219 self.downgrade
220 }
221 pub fn set_downgrade(&mut self, v: bool) -> &mut Self {
227 self.downgrade = v;
228 self
229 }
230 pub fn downgrade(mut self, v: bool) -> Self {
236 self.downgrade = v;
237 self
238 }
239
240 #[cfg_attr(
246 feature = "lt2014_2",
247 doc = "flag to force inclusion of wildcards in filenames. See the",
248 doc = "File Specifications chapter for details."
249 )]
250 #[cfg_attr(
251 all(feature = "lt2015_1", not(feature = "lt2014_2")),
252 doc = "option to force inclusion of wildcards in filenames. See the",
253 doc = "“File Specifications” chapter for details."
254 )]
255 #[cfg_attr(
256 all(feature = "lt2017_1", not(feature = "lt2015_1")),
257 doc = "option to force inclusion of wildcards in filenames. See",
258 doc = "“File Specifications” for details."
259 )]
260 #[cfg_attr(
261 all(feature = "lt2018_2", not(feature = "lt2017_1")),
262 doc = "option to force inclusion of wildcards in filenames. See File",
263 doc = "Specifications for details."
264 )]
265 #[cfg_attr(
266 all(feature = "lt2025_1", not(feature = "lt2018_2")),
267 doc = "option to force inclusion of wildcards in filenames. See File",
268 doc = "specifications for details."
269 )]
270 #[cfg_attr(
271 not(feature = "lt2025_1"),
272 doc = "option to force inclusion of wildcards in filenames. See File",
273 doc = "specifications for details.",
274 doc = "",
275 doc = "Filenames that contain the special characters '@', '#', '%', or '*' are",
276 doc = "reformatted to encode the characters using ASCII hexadecimal",
277 doc = "representation. After the files are added, refer to them using the",
278 doc = "reformatted file name instead of the local file system name."
279 )]
280 pub fn get_force_literal_filenames(&self) -> bool {
281 self.force_literal_filenames
282 }
283
284 #[cfg_attr(
290 feature = "lt2014_2",
291 doc = "flag to force inclusion of wildcards in filenames. See the",
292 doc = "File Specifications chapter for details."
293 )]
294 #[cfg_attr(
295 all(feature = "lt2015_1", not(feature = "lt2014_2")),
296 doc = "option to force inclusion of wildcards in filenames. See the",
297 doc = "“File Specifications” chapter for details."
298 )]
299 #[cfg_attr(
300 all(feature = "lt2017_1", not(feature = "lt2015_1")),
301 doc = "option to force inclusion of wildcards in filenames. See",
302 doc = "“File Specifications” for details."
303 )]
304 #[cfg_attr(
305 all(feature = "lt2018_2", not(feature = "lt2017_1")),
306 doc = "option to force inclusion of wildcards in filenames. See File",
307 doc = "Specifications for details."
308 )]
309 #[cfg_attr(
310 all(feature = "lt2025_1", not(feature = "lt2018_2")),
311 doc = "option to force inclusion of wildcards in filenames. See File",
312 doc = "specifications for details."
313 )]
314 #[cfg_attr(
315 not(feature = "lt2025_1"),
316 doc = "option to force inclusion of wildcards in filenames. See File",
317 doc = "specifications for details.",
318 doc = "",
319 doc = "Filenames that contain the special characters '@', '#', '%', or '*' are",
320 doc = "reformatted to encode the characters using ASCII hexadecimal",
321 doc = "representation. After the files are added, refer to them using the",
322 doc = "reformatted file name instead of the local file system name."
323 )]
324 pub fn set_force_literal_filenames(&mut self, v: bool) -> &mut Self {
325 self.force_literal_filenames = v;
326 self
327 }
328
329 #[cfg_attr(
335 feature = "lt2014_2",
336 doc = "flag to force inclusion of wildcards in filenames. See the",
337 doc = "File Specifications chapter for details."
338 )]
339 #[cfg_attr(
340 all(feature = "lt2015_1", not(feature = "lt2014_2")),
341 doc = "option to force inclusion of wildcards in filenames. See the",
342 doc = "“File Specifications” chapter for details."
343 )]
344 #[cfg_attr(
345 all(feature = "lt2017_1", not(feature = "lt2015_1")),
346 doc = "option to force inclusion of wildcards in filenames. See",
347 doc = "“File Specifications” for details."
348 )]
349 #[cfg_attr(
350 all(feature = "lt2018_2", not(feature = "lt2017_1")),
351 doc = "option to force inclusion of wildcards in filenames. See File",
352 doc = "Specifications for details."
353 )]
354 #[cfg_attr(
355 all(feature = "lt2025_1", not(feature = "lt2018_2")),
356 doc = "option to force inclusion of wildcards in filenames. See File",
357 doc = "specifications for details."
358 )]
359 #[cfg_attr(
360 not(feature = "lt2025_1"),
361 doc = "option to force inclusion of wildcards in filenames. See File",
362 doc = "specifications for details.",
363 doc = "",
364 doc = "Filenames that contain the special characters '@', '#', '%', or '*' are",
365 doc = "reformatted to encode the characters using ASCII hexadecimal",
366 doc = "representation. After the files are added, refer to them using the",
367 doc = "reformatted file name instead of the local file system name."
368 )]
369 pub fn force_literal_filenames(mut self, v: bool) -> Self {
370 self.force_literal_filenames = v;
371 self
372 }
373
374 pub fn get_skip_ignore(&self) -> bool {
381 self.skip_ignore
382 }
383 pub fn set_skip_ignore(&mut self, v: bool) -> &mut Self {
390 self.skip_ignore = v;
391 self
392 }
393 pub fn skip_ignore(mut self, v: bool) -> Self {
400 self.skip_ignore = v;
401 self
402 }
403
404 pub fn get_preview(&self) -> bool {
411 self.preview
412 }
413 pub fn set_preview(&mut self, v: bool) -> &mut Self {
420 self.preview = v;
421 self
422 }
423 pub fn preview(mut self, v: bool) -> Self {
430 self.preview = v;
431 self
432 }
433
434 #[cfg_attr(
441 feature = "lt2014_2",
442 doc = "Please see the File Types chapter for a list of Perforce",
443 doc = "file types."
444 )]
445 #[cfg_attr(
446 all(feature = "lt2015_1", not(feature = "lt2014_2")),
447 doc = "Please see the “File Types” chapter for a list of",
448 doc = "Perforce file types."
449 )]
450 #[cfg_attr(
451 all(feature = "lt2017_1", not(feature = "lt2015_1")),
452 doc = "See “File Types” for a list of Perforce file types."
453 )]
454 #[cfg_attr(
455 all(feature = "lt2017_2", not(feature = "lt2017_1")),
456 doc = "See File Types for a list of Perforce file types."
457 )]
458 #[cfg_attr(
459 all(feature = "lt2018_2", not(feature = "lt2017_2")),
460 doc = "See File Types for a list of Helix Server file types."
461 )]
462 #[cfg_attr(
463 all(feature = "lt2019_1", not(feature = "lt2018_2")),
464 doc = "See File types for a list of Helix Server file types."
465 )]
466 #[cfg_attr(
467 all(feature = "lt2021_1", not(feature = "lt2019_1")),
468 doc = "See File types for a list of Helix server file types."
469 )]
470 #[cfg_attr(
471 all(feature = "lt2024_1", not(feature = "lt2021_1")),
472 doc = "See File types for a list of Helix Server file types."
473 )]
474 #[cfg_attr(
475 all(feature = "lt2025_2", not(feature = "lt2024_1")),
476 doc = "See File types as well as the lbr.autocompress",
477 doc = "configurable."
478 )]
479 #[cfg_attr(
480 not(feature = "lt2025_2"),
481 doc = "See Base file types and modifiers as well as the",
482 doc = "lbr.autocompress configurable."
483 )]
484 pub fn get_filetype(&self) -> Option<&String> {
485 self.filetype.as_ref()
486 }
487
488 #[cfg_attr(
495 feature = "lt2014_2",
496 doc = "Please see the File Types chapter for a list of Perforce",
497 doc = "file types."
498 )]
499 #[cfg_attr(
500 all(feature = "lt2015_1", not(feature = "lt2014_2")),
501 doc = "Please see the “File Types” chapter for a list of",
502 doc = "Perforce file types."
503 )]
504 #[cfg_attr(
505 all(feature = "lt2017_1", not(feature = "lt2015_1")),
506 doc = "See “File Types” for a list of Perforce file types."
507 )]
508 #[cfg_attr(
509 all(feature = "lt2017_2", not(feature = "lt2017_1")),
510 doc = "See File Types for a list of Perforce file types."
511 )]
512 #[cfg_attr(
513 all(feature = "lt2018_2", not(feature = "lt2017_2")),
514 doc = "See File Types for a list of Helix Server file types."
515 )]
516 #[cfg_attr(
517 all(feature = "lt2019_1", not(feature = "lt2018_2")),
518 doc = "See File types for a list of Helix Server file types."
519 )]
520 #[cfg_attr(
521 all(feature = "lt2021_1", not(feature = "lt2019_1")),
522 doc = "See File types for a list of Helix server file types."
523 )]
524 #[cfg_attr(
525 all(feature = "lt2024_1", not(feature = "lt2021_1")),
526 doc = "See File types for a list of Helix Server file types."
527 )]
528 #[cfg_attr(
529 all(feature = "lt2025_2", not(feature = "lt2024_1")),
530 doc = "See File types as well as the lbr.autocompress",
531 doc = "configurable."
532 )]
533 #[cfg_attr(
534 not(feature = "lt2025_2"),
535 doc = "See Base file types and modifiers as well as the",
536 doc = "lbr.autocompress configurable."
537 )]
538 pub fn set_filetype(&mut self, v: impl Into<String>) -> &mut Self {
539 self.filetype = Some(v.into());
540 self
541 }
542
543 #[cfg_attr(
550 feature = "lt2014_2",
551 doc = "Please see the File Types chapter for a list of Perforce",
552 doc = "file types."
553 )]
554 #[cfg_attr(
555 all(feature = "lt2015_1", not(feature = "lt2014_2")),
556 doc = "Please see the “File Types” chapter for a list of",
557 doc = "Perforce file types."
558 )]
559 #[cfg_attr(
560 all(feature = "lt2017_1", not(feature = "lt2015_1")),
561 doc = "See “File Types” for a list of Perforce file types."
562 )]
563 #[cfg_attr(
564 all(feature = "lt2017_2", not(feature = "lt2017_1")),
565 doc = "See File Types for a list of Perforce file types."
566 )]
567 #[cfg_attr(
568 all(feature = "lt2018_2", not(feature = "lt2017_2")),
569 doc = "See File Types for a list of Helix Server file types."
570 )]
571 #[cfg_attr(
572 all(feature = "lt2019_1", not(feature = "lt2018_2")),
573 doc = "See File types for a list of Helix Server file types."
574 )]
575 #[cfg_attr(
576 all(feature = "lt2021_1", not(feature = "lt2019_1")),
577 doc = "See File types for a list of Helix server file types."
578 )]
579 #[cfg_attr(
580 all(feature = "lt2024_1", not(feature = "lt2021_1")),
581 doc = "See File types for a list of Helix Server file types."
582 )]
583 #[cfg_attr(
584 all(feature = "lt2025_2", not(feature = "lt2024_1")),
585 doc = "See File types as well as the lbr.autocompress",
586 doc = "configurable."
587 )]
588 #[cfg_attr(
589 not(feature = "lt2025_2"),
590 doc = "See Base file types and modifiers as well as the",
591 doc = "lbr.autocompress configurable."
592 )]
593 pub fn filetype(mut self, v: impl Into<String>) -> Self {
594 self.filetype = Some(v.into());
595 self
596 }
597}
598
599#[cfg(test)]
600mod tests {
601 use super::*;
602 use crate::cmd::args_of;
603
604 #[test]
607 fn without_options() {
608 let add = Add::new("p4", GlobalOpts::new());
609
610 let command = add.setup_command("p4");
611
612 assert_eq!(command.get_program(), OsStr::new("p4"));
613 assert_eq!(args_of(&command), ["add"]);
614 }
615
616 #[test]
617 fn all_local_options() {
618 let mut add = Add::new("p4", GlobalOpts::new());
619 add.set_change_list("42")
620 .set_downgrade(true)
621 .set_force_literal_filenames(true)
622 .set_skip_ignore(true)
623 .set_preview(true)
624 .set_filetype("text");
625
626 assert_eq!(
627 args_of(&add.setup_command("p4")),
628 ["add", "-c", "42", "-d", "-f", "-I", "-n", "-t", "text"]
629 );
630 }
631}