Skip to main content

perforce_cli/cmd/
print.rs

1use std::{
2    ffi::OsStr,
3    path::PathBuf,
4    process::{Child, Command, Stdio},
5};
6
7use super::{ExclusiveOption, SubCommand, Unselected};
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12/// Standard print mode of `p4 print`: print the contents of depot file
13/// revisions.
14///
15/// Entered by setting any standard option (such as [`Print::all_revisions`])
16/// from the [`Unselected`] state.
17#[derive(Debug, Clone, Default)]
18pub struct StandardPrintMode {
19    all_revisions: bool,
20
21    from_archive_depots: bool,
22
23    suppress_keyword_expansion: bool,
24
25    #[cfg(not(feature = "lt2023_1"))]
26    line_ending: Option<LineEnding>,
27
28    #[cfg(not(feature = "lt2023_1"))]
29    charset: Option<String>,
30
31    #[cfg(not(feature = "lt2023_1"))]
32    utf8bom: Option<Utf8Bom>,
33
34    limit: Option<u64>,
35
36    #[cfg(not(feature = "lt2022_1"))]
37    offset: Option<u64>,
38
39    #[cfg(not(feature = "lt2022_1"))]
40    size: Option<u64>,
41
42    redirect_output: Option<PathBuf>,
43
44    quiet_mode: bool,
45
46    #[cfg(not(feature = "lt2026_1"))]
47    ignore_changeview: bool,
48}
49
50impl ExclusiveOption for StandardPrintMode {
51    fn inject_args(&self, command: &mut Command) {
52        if self.all_revisions {
53            command.arg("-a");
54        }
55
56        if self.from_archive_depots {
57            command.arg("-A");
58        }
59
60        if self.suppress_keyword_expansion {
61            #[cfg(feature = "lt2022_1")]
62            {
63                command.arg("-k");
64            }
65            #[cfg(not(feature = "lt2022_1"))]
66            {
67                command.arg("-K");
68            }
69        }
70
71        if let Some(output) = &self.redirect_output {
72            command.arg("-o").arg(output);
73        }
74
75        if self.quiet_mode {
76            command.arg("-q");
77        }
78
79        if let Some(max) = self.limit {
80            command.arg("-m").arg(max.to_string());
81        }
82
83        #[cfg(not(feature = "lt2022_1"))]
84        {
85            if let Some(offset) = self.offset {
86                command.arg("--offset").arg(offset.to_string());
87            }
88
89            if let Some(size) = self.size {
90                command.arg("--size").arg(size.to_string());
91            }
92        }
93
94        #[cfg(not(feature = "lt2023_1"))]
95        {
96            if let Some(charset) = &self.charset {
97                command.arg("-Q").arg(charset);
98            }
99
100            if let Some(utf8bom) = &self.utf8bom {
101                command.arg("-B").arg(utf8bom.as_str());
102            }
103
104            if let Some(line_ending) = &self.line_ending {
105                command.arg("-L").arg(line_ending.as_str());
106            }
107        }
108
109        #[cfg(not(feature = "lt2026_1"))]
110        {
111            if self.ignore_changeview {
112                command.arg("--ignore-changeview");
113            }
114        }
115    }
116}
117
118/// Unload depot mode of `p4 print` (`-U`): look for the specified files
119/// in the unload depot.
120///
121/// Entered with [`Print::from_unload_depot`]. No other local option can be
122/// combined with `-U`.
123#[derive(Debug, Clone, Copy, Default)]
124pub struct UnloadDepotMode;
125
126impl ExclusiveOption for UnloadDepotMode {
127    fn inject_args(&self, command: &mut Command) {
128        command.arg("-U");
129    }
130}
131
132/// Attribute trait mode of `p4 print` (`-T attribute`): print the value of
133/// the specified non-encoded attribute of the specified file.
134///
135/// Only `-a`, `-q`, `-o`, and (before 2026.1) `--ignore-changeview` can be
136/// combined with `-T`. Entered with [`Print::extract_attribute`].
137#[cfg(not(feature = "lt2024_2"))]
138#[derive(Debug, Clone, Default)]
139pub struct AttributeTraitMode {
140    all_revisions: bool,
141
142    redirect_output: Option<PathBuf>,
143
144    quiet_mode: bool,
145
146    attribute: String,
147
148    #[cfg(not(feature = "lt2026_1"))]
149    ignore_changeview: bool,
150}
151
152#[cfg(not(feature = "lt2024_2"))]
153impl ExclusiveOption for AttributeTraitMode {
154    fn inject_args(&self, command: &mut Command) {
155        command.arg("-T").arg(&self.attribute);
156
157        if self.all_revisions {
158            command.arg("-a");
159        }
160
161        if let Some(output) = &self.redirect_output {
162            command.arg("-o").arg(output);
163        }
164
165        if self.quiet_mode {
166            command.arg("-q");
167        }
168
169        #[cfg(not(feature = "lt2026_1"))]
170        {
171            if self.ignore_changeview {
172                command.arg("--ignore-changeview");
173            }
174        }
175    }
176}
177
178// https://help.perforce.com/helix-core/integrations-plugins/p4jenkins/current/Content/P4Jenkins/unicode.html
179/// The `-B` setting controlling the byte-order-mark in utf8 files.
180#[cfg(not(feature = "lt2023_1"))]
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum Utf8Bom {
183    /// Do not write a BOM (`-B 0`).
184    No,
185    /// Write utf8 files with a BOM (`-B 1`).
186    Yes,
187    /// Write the BOM only on Windows (`-B 2`).
188    WindowsOnly,
189}
190
191#[cfg(not(feature = "lt2023_1"))]
192impl Utf8Bom {
193    /// Returns the command-line value for this BOM setting.
194    pub fn as_str(&self) -> &'static str {
195        match self {
196            Utf8Bom::No => "0",
197            Utf8Bom::Yes => "1",
198            Utf8Bom::WindowsOnly => "2",
199        }
200    }
201}
202
203/// The `-L` line ending for textual files: 'unix', 'win', or 'mac'.
204#[cfg(not(feature = "lt2023_1"))]
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub enum LineEnding {
207    /// Use the `unix` line ending (`-L unix`).
208    Unix,
209    /// Use the `win` line ending (`-L win`).
210    Win,
211    /// Use the `mac` line ending (`-L mac`).
212    Mac,
213}
214
215#[cfg(not(feature = "lt2023_1"))]
216impl LineEnding {
217    /// Returns the command-line value for this line ending.
218    pub fn as_str(&self) -> &'static str {
219        match self {
220            LineEnding::Unix => "unix",
221            LineEnding::Win => "win",
222            LineEnding::Mac => "mac",
223        }
224    }
225}
226
227#[cfg_attr(
228    feature = "lt2015_1",
229    doc = "`p4 [g-opts] print [-a -A -k -o outfile -q -m max -U] file[revRange] ...`"
230)]
231#[cfg_attr(
232    all(feature = "lt2016_1", not(feature = "lt2015_1")),
233    doc = "`p4 [g-opts] print [-a -A -k -q] [-m max] [-o outfile] file[revRange] …`",
234    doc = "",
235    doc = "`p4 [g-opts] print -U unloadfile …`"
236)]
237#[cfg_attr(
238    all(feature = "lt2018_1", not(feature = "lt2016_1")),
239    doc = "`p4 [g-opts] print [-a -A -k -q] [-m max] [-o outfile] file[revRange] ...`",
240    doc = "",
241    doc = "`p4 [g-opts] print -U unloadfile ...`"
242)]
243#[cfg_attr(
244    all(feature = "lt2022_1", not(feature = "lt2018_1")),
245    doc = "`p4 [g-opts] print [-a -A -k -q] [-m max] [-o outfile] FileSpec[revSpec]`",
246    doc = "",
247    doc = "`p4 [g-opts] print -U unload[FileSpec]`"
248)]
249#[cfg_attr(
250    all(feature = "lt2023_1", not(feature = "lt2022_1")),
251    doc = "`p4 [g-opts] print [-a -A -K -q] [-m max] --offset bytesToSkip --size bytesToPrint [-o outfile] FileSpec[revSpec]`",
252    doc = "",
253    doc = "`p4 [g-opts] print -U unload[FileSpec]`"
254)]
255#[cfg_attr(
256    all(feature = "lt2024_2", not(feature = "lt2023_1")),
257    doc = "`p4 print [-a -A -K -o localFile -q -m max --offset offset --size size -Q charset -B utf8bom -L line-ending] file[revRange] ...`",
258    doc = "",
259    doc = "`p4 print -U unloadfile ...`"
260)]
261#[cfg_attr(
262    all(feature = "lt2026_1", not(feature = "lt2024_2")),
263    doc = "`p4 print [-a -A -K -o localFile -q -m max --offset offset --size size -Q charset -B utf8bom -L line-ending] file[revRange] ...`",
264    doc = "",
265    doc = "`p4 print -U unloadfile ...`",
266    doc = "",
267    doc = "`p4 print -T attribute [-a -q -o localFile] file ...`"
268)]
269#[cfg_attr(
270    not(feature = "lt2026_1"),
271    doc = "`p4 print [-a -A -K -o localFile -q -m max --offset offset --size size -Q charset -B utf8bom -L line-ending] [--ignore-changeview] file[revRange] ...`",
272    doc = "",
273    doc = "`p4 print -U unloadfile ...`",
274    doc = "",
275    doc = "`p4 print -T attribute [-a -q -o localFile] [--ignore-changeview] file ...`"
276)]
277///
278/// Print the contents of a depot file revision.
279///
280/// The `M` type parameter tracks which of the three forms of the command
281/// is in use at compile time. The default [`Unselected`] state prints
282/// files without local options; setting any standard option such as
283/// [`Self::all_revisions`] transitions to [`StandardPrintMode`],
284/// [`Self::from_unload_depot`] transitions to [`UnloadDepotMode`], and
285/// [`Self::extract_attribute`] transitions to [`AttributeTraitMode`].
286#[derive(Debug, Clone, Default)]
287pub struct Print<M = Unselected> {
288    bin: PathBuf,
289
290    global_opts: GlobalOpts,
291
292    mode: M,
293}
294
295impl Print<Unselected> {
296    /// Creates a new `p4 print` command.
297    ///
298    /// `bin` is the path to the Perforce command-line executable.
299    pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
300        Self {
301            bin: bin.into(),
302            global_opts,
303            mode: Unselected,
304        }
305    }
306
307    /// # Description
308    ///
309    /// -U
310    ///
311    /// Look for the specified file or files in the unload depot. Data about an
312    /// unloaded client, label, or task stream can be printed.
313    ///
314    /// Transitions this command to the [`UnloadDepotMode`] state; no other
315    /// local option can be combined with `-U`.
316    pub fn from_unload_depot(self) -> Print<UnloadDepotMode> {
317        Print {
318            bin: self.bin,
319            global_opts: self.global_opts,
320            mode: UnloadDepotMode,
321        }
322    }
323
324    /// # Description
325    ///
326    /// -T attribute
327    ///
328    /// Print the value of the specified non-encoded attribute of the specified
329    /// file. This command, rather than `p4 fstat -Oa`, is appropriate for
330    /// non-encoded binary attributes larger than 250 megabytes. The
331    /// `p4 fstat -Oa` command might fail with the `Rpc buffer too big to send`
332    /// error if attributes exceed 250 megabytes. The `p4 print` command has no
333    /// option to show the value of the attribute in hex. For that, use the
334    /// `p4 fstat -Oe` command.
335    ///
336    /// Transitions this command to the [`AttributeTraitMode`] state.
337    #[cfg(not(feature = "lt2024_2"))]
338    pub fn extract_attribute(self, name: impl Into<String>) -> Print<AttributeTraitMode> {
339        Print {
340            bin: self.bin,
341            global_opts: self.global_opts,
342            mode: AttributeTraitMode {
343                all_revisions: false,
344                redirect_output: None,
345                quiet_mode: false,
346                attribute: name.into(),
347                #[cfg(not(feature = "lt2026_1"))]
348                ignore_changeview: false,
349            },
350        }
351    }
352
353    /// # Description
354    ///
355    /// -a
356    ///
357    /// For each file, print all revisions within a specified revision range,
358    /// rather than only the highest revision in the range.
359    ///
360    /// Transitions this command to the [`StandardPrintMode`] state.
361    pub fn all_revisions(self, v: bool) -> Print<StandardPrintMode> {
362        Print {
363            bin: self.bin,
364            global_opts: self.global_opts,
365            mode: StandardPrintMode {
366                all_revisions: v,
367                ..StandardPrintMode::default()
368            },
369        }
370    }
371
372    /// # Description
373    ///
374    /// -A
375    ///
376    #[cfg_attr(
377        feature = "lt2023_1",
378        doc = "Attempt to print a file stored in an archive depot."
379    )]
380    #[cfg_attr(not(feature = "lt2023_1"), doc = "Print files in archive depots.")]
381    ///
382    /// Transitions this command to the [`StandardPrintMode`] state.
383    pub fn archive_depots(self, v: bool) -> Print<StandardPrintMode> {
384        Print {
385            bin: self.bin,
386            global_opts: self.global_opts,
387            mode: StandardPrintMode {
388                from_archive_depots: v,
389                ..StandardPrintMode::default()
390            },
391        }
392    }
393
394    /// # Description
395    ///
396    #[cfg_attr(feature = "lt2022_1", doc = "-k")]
397    #[cfg_attr(not(feature = "lt2022_1"), doc = "-K")]
398    ///
399    #[cfg_attr(feature = "lt2023_1", doc = "Suppress RCS keyword expansion.")]
400    #[cfg_attr(
401        not(feature = "lt2023_1"),
402        doc = "Suppress RCS keyword expansion. This replaced the `-k` flag in",
403        doc = "2022.1, which is now an alias for `-K` for backwards compatibility."
404    )]
405    ///
406    /// Transitions this command to the [`StandardPrintMode`] state.
407    pub fn suppress_keyword_expansion(self, v: bool) -> Print<StandardPrintMode> {
408        Print {
409            bin: self.bin,
410            global_opts: self.global_opts,
411            mode: StandardPrintMode {
412                suppress_keyword_expansion: v,
413                ..StandardPrintMode::default()
414            },
415        }
416    }
417
418    /// # Description
419    ///
420    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
421    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
422    ///
423    #[cfg_attr(
424        feature = "lt2018_2",
425        doc = "Redirect output to the specified output file on the local disk,",
426        doc = "preserving the same file type, attributes, and/or permission bits",
427        doc = "as the original file in the depot."
428    )]
429    #[cfg_attr(
430        all(feature = "lt2023_1", not(feature = "lt2018_2")),
431        doc = "Redirect output to the specified output file (`outfile`) on the",
432        doc = "local disk. This preserves the same file type, attributes, and/or",
433        doc = "permission bits as the original file (`FileSpec`) in the depot.",
434        doc = "Multiple files can be written by using wildcards in the",
435        doc = "`localFile` argument that match wildcards in the depot",
436        doc = "(`FileSpec`) argument. For example: To print the contents of a",
437        doc = "directory and directories under that directory, use the `...`",
438        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
439        doc = "print all files that match readme.txt or readme.pdf, you might",
440        doc = "specify `p4 print -o readme.* //depot/readme.*`"
441    )]
442    #[cfg_attr(
443        not(feature = "lt2023_1"),
444        doc = "Redirect output to the specified output file (`localfile`) on the",
445        doc = "local disk. This preserves the same file type, attributes, and/or",
446        doc = "permission bits as the original file (`FileSpec`) in the depot.",
447        doc = "Multiple files can be written by using wildcards in the",
448        doc = "`localFile` argument that match wildcards in the depot",
449        doc = "(`FileSpec`) argument. For example: To print the contents of a",
450        doc = "directory and directories under that directory, use the `...`",
451        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
452        doc = "print all files that match readme.txt or readme.pdf, you might",
453        doc = "specify `p4 print -o readme.* //depot/readme.*`"
454    )]
455    ///
456    /// Transitions this command to the [`StandardPrintMode`] state.
457    pub fn redirect_output(self, v: impl Into<PathBuf>) -> Print<StandardPrintMode> {
458        Print {
459            bin: self.bin,
460            global_opts: self.global_opts,
461            mode: StandardPrintMode {
462                redirect_output: Some(v.into()),
463                ..StandardPrintMode::default()
464            },
465        }
466    }
467
468    /// # Description
469    ///
470    /// -q
471    ///
472    /// Suppress the one-line file header normally added by
473    #[cfg_attr(feature = "lt2018_1", doc = "Perforce.")]
474    #[cfg_attr(
475        all(feature = "lt2019_1", not(feature = "lt2018_1")),
476        doc = "Helix Server."
477    )]
478    #[cfg_attr(
479        all(feature = "lt2021_1", not(feature = "lt2019_1")),
480        doc = "Helix server."
481    )]
482    #[cfg_attr(
483        all(feature = "lt2024_1", not(feature = "lt2021_1")),
484        doc = "Helix Server."
485    )]
486    #[cfg_attr(
487        all(feature = "lt2024_2", not(feature = "lt2024_1")),
488        doc = "Helix Core Server."
489    )]
490    #[cfg_attr(not(feature = "lt2024_2"), doc = "P4 Server.")]
491    ///
492    /// Transitions this command to the [`StandardPrintMode`] state.
493    pub fn quiet_mode(self, v: bool) -> Print<StandardPrintMode> {
494        Print {
495            bin: self.bin,
496            global_opts: self.global_opts,
497            mode: StandardPrintMode {
498                quiet_mode: v,
499                ..StandardPrintMode::default()
500            },
501        }
502    }
503
504    /// # Description
505    ///
506    /// -m max
507    ///
508    /// Print only the first *max* files.
509    ///
510    /// Transitions this command to the [`StandardPrintMode`] state.
511    pub fn limit(self, v: u64) -> Print<StandardPrintMode> {
512        Print {
513            bin: self.bin,
514            global_opts: self.global_opts,
515            mode: StandardPrintMode {
516                limit: Some(v),
517                ..StandardPrintMode::default()
518            },
519        }
520    }
521
522    /// # Description
523    ///
524    /// --offset bytesToSkip
525    ///
526    /// (Optional) Skip the specified number of bytes and only print what
527    /// follows. Can be used with `--size`.
528    ///
529    /// Transitions this command to the [`StandardPrintMode`] state.
530    #[cfg(not(feature = "lt2022_1"))]
531    pub fn offset(self, v: u64) -> Print<StandardPrintMode> {
532        Print {
533            bin: self.bin,
534            global_opts: self.global_opts,
535            mode: StandardPrintMode {
536                offset: Some(v),
537                ..StandardPrintMode::default()
538            },
539        }
540    }
541
542    /// # Description
543    ///
544    /// --size bytesToPrint
545    ///
546    /// (Optional) Print the specified number of bytes from the offset. If
547    /// `--offset` is not explicitly set, prints the specified number of bytes
548    /// from the beginning of the file.
549    ///
550    /// Transitions this command to the [`StandardPrintMode`] state.
551    #[cfg(not(feature = "lt2022_1"))]
552    pub fn size(self, v: u64) -> Print<StandardPrintMode> {
553        Print {
554            bin: self.bin,
555            global_opts: self.global_opts,
556            mode: StandardPrintMode {
557                size: Some(v),
558                ..StandardPrintMode::default()
559            },
560        }
561    }
562
563    /// # Description
564    ///
565    /// -Q charset
566    ///
567    /// Allow the charset for unicode type files to be explicitly specified,
568    /// overriding the connection's `P4CHARSET` but not overriding the charset
569    /// of unicode files with versioned charsets.
570    ///
571    /// Transitions this command to the [`StandardPrintMode`] state.
572    #[cfg(not(feature = "lt2023_1"))]
573    pub fn charset(self, v: impl Into<String>) -> Print<StandardPrintMode> {
574        Print {
575            bin: self.bin,
576            global_opts: self.global_opts,
577            mode: StandardPrintMode {
578                charset: Some(v.into()),
579                ..StandardPrintMode::default()
580            },
581        }
582    }
583
584    /// # Description
585    ///
586    /// -B 1 / -B 0
587    ///
588    /// Override the client-side `filesys.utf8bom` setting, controlling the
589    /// presence of the byte-order-mark in utf8 type files. With `true`,
590    /// write utf8 files with a BOM (`-B 1`); with `false`, do not write a
591    /// BOM (`-B 0`).
592    ///
593    /// Transitions this command to the [`StandardPrintMode`] state.
594    #[cfg(not(feature = "lt2023_1"))]
595    pub fn write_utf8bom(self, v: bool) -> Print<StandardPrintMode> {
596        Print {
597            bin: self.bin,
598            global_opts: self.global_opts,
599            mode: StandardPrintMode {
600                utf8bom: Some(if v { Utf8Bom::Yes } else { Utf8Bom::No }),
601                ..StandardPrintMode::default()
602            },
603        }
604    }
605
606    /// # Description
607    ///
608    /// -B 2
609    ///
610    /// Override the client-side `filesys.utf8bom` setting to write the BOM
611    /// only on Windows.
612    ///
613    /// Transitions this command to the [`StandardPrintMode`] state.
614    #[cfg(not(feature = "lt2023_1"))]
615    pub fn write_utf8bom_windows_only(self) -> Print<StandardPrintMode> {
616        Print {
617            bin: self.bin,
618            global_opts: self.global_opts,
619            mode: StandardPrintMode {
620                utf8bom: Some(Utf8Bom::WindowsOnly),
621                ..StandardPrintMode::default()
622            },
623        }
624    }
625
626    /// # Description
627    ///
628    /// -L unix
629    ///
630    /// Allow the line ending of textual files to be explicitly specified as
631    /// 'unix', 'win', or 'mac'.
632    ///
633    /// Transitions this command to the [`StandardPrintMode`] state.
634    #[cfg(not(feature = "lt2023_1"))]
635    pub fn line_ending_unix(self) -> Print<StandardPrintMode> {
636        Print {
637            bin: self.bin,
638            global_opts: self.global_opts,
639            mode: StandardPrintMode {
640                line_ending: Some(LineEnding::Unix),
641                ..StandardPrintMode::default()
642            },
643        }
644    }
645
646    /// # Description
647    ///
648    /// -L win
649    ///
650    /// Allow the line ending of textual files to be explicitly specified as
651    /// 'unix', 'win', or 'mac'.
652    ///
653    /// Transitions this command to the [`StandardPrintMode`] state.
654    #[cfg(not(feature = "lt2023_1"))]
655    pub fn line_ending_win(self) -> Print<StandardPrintMode> {
656        Print {
657            bin: self.bin,
658            global_opts: self.global_opts,
659            mode: StandardPrintMode {
660                line_ending: Some(LineEnding::Win),
661                ..StandardPrintMode::default()
662            },
663        }
664    }
665
666    /// # Description
667    ///
668    /// -L mac
669    ///
670    /// Allow the line ending of textual files to be explicitly specified as
671    /// 'unix', 'win', or 'mac'.
672    ///
673    /// Transitions this command to the [`StandardPrintMode`] state.
674    #[cfg(not(feature = "lt2023_1"))]
675    pub fn line_ending_mac(self) -> Print<StandardPrintMode> {
676        Print {
677            bin: self.bin,
678            global_opts: self.global_opts,
679            mode: StandardPrintMode {
680                line_ending: Some(LineEnding::Mac),
681                ..StandardPrintMode::default()
682            },
683        }
684    }
685
686    /// # Description
687    ///
688    /// --ignore-changeview
689    ///
690    /// Remove the changelist limit on depot paths. See ChangeView in
691    /// `p4 client`.
692    ///
693    /// Transitions this command to the [`StandardPrintMode`] state.
694    #[cfg(not(feature = "lt2026_1"))]
695    pub fn ignore_changeview(self, v: bool) -> Print<StandardPrintMode> {
696        Print {
697            bin: self.bin,
698            global_opts: self.global_opts,
699            mode: StandardPrintMode {
700                ignore_changeview: v,
701                ..StandardPrintMode::default()
702            },
703        }
704    }
705}
706
707impl<M: ExclusiveOption, S, I> ParameterizedSpawn<(S,)> for Print<M>
708where
709    S: IntoIterator<Item = I>,
710    I: AsRef<OsStr>,
711{
712    type Output = Child;
713    type Error = std::io::Error;
714
715    /// Spawns `p4 print` for the given files as a child process with piped
716    /// standard output and error streams; use the returned [`Child`] handle
717    /// to wait for it or interact with it.
718    fn spawn_with(&mut self, (files,): (S,)) -> Result<Self::Output, Self::Error> {
719        self.setup_command(&self.bin)
720            .args(files)
721            .stdout(Stdio::piped())
722            .stderr(Stdio::piped())
723            .spawn()
724    }
725}
726
727impl<M: ExclusiveOption> Print<M> {
728    /// # Description
729    ///
730    /// g-opts
731    ///
732    #[cfg_attr(
733        feature = "lt2014_2",
734        doc = "See the [Global Options](GlobalOpts) section."
735    )]
736    #[cfg_attr(
737        all(feature = "lt2015_1", not(feature = "lt2014_2")),
738        doc = "See the [“Global Options”](GlobalOpts) section."
739    )]
740    #[cfg_attr(
741        all(feature = "lt2017_1", not(feature = "lt2015_1")),
742        doc = "See [“Global Options”](GlobalOpts)."
743    )]
744    #[cfg_attr(
745        all(feature = "lt2018_2", not(feature = "lt2017_1")),
746        doc = "See [Global Options](GlobalOpts)."
747    )]
748    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
749    pub fn get_global_opts(&self) -> &GlobalOpts {
750        &self.global_opts
751    }
752
753    /// # Description
754    ///
755    /// g-opts
756    ///
757    #[cfg_attr(
758        feature = "lt2014_2",
759        doc = "See the [Global Options](GlobalOpts) section."
760    )]
761    #[cfg_attr(
762        all(feature = "lt2015_1", not(feature = "lt2014_2")),
763        doc = "See the [“Global Options”](GlobalOpts) section."
764    )]
765    #[cfg_attr(
766        all(feature = "lt2017_1", not(feature = "lt2015_1")),
767        doc = "See [“Global Options”](GlobalOpts)."
768    )]
769    #[cfg_attr(
770        all(feature = "lt2018_2", not(feature = "lt2017_1")),
771        doc = "See [Global Options](GlobalOpts)."
772    )]
773    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
774    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
775        self.global_opts = v;
776        self
777    }
778
779    /// # Description
780    ///
781    /// g-opts
782    ///
783    #[cfg_attr(
784        feature = "lt2014_2",
785        doc = "See the [Global Options](GlobalOpts) section."
786    )]
787    #[cfg_attr(
788        all(feature = "lt2015_1", not(feature = "lt2014_2")),
789        doc = "See the [“Global Options”](GlobalOpts) section."
790    )]
791    #[cfg_attr(
792        all(feature = "lt2017_1", not(feature = "lt2015_1")),
793        doc = "See [“Global Options”](GlobalOpts)."
794    )]
795    #[cfg_attr(
796        all(feature = "lt2018_2", not(feature = "lt2017_1")),
797        doc = "See [Global Options](GlobalOpts)."
798    )]
799    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
800    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
801        self.global_opts = v;
802        self
803    }
804}
805
806impl Print<StandardPrintMode> {
807    /// # Description
808    ///
809    /// -a
810    ///
811    /// For each file, print all revisions within a specified revision range,
812    /// rather than only the highest revision in the range.
813    pub fn get_all_revisions(&self) -> bool {
814        self.mode.all_revisions
815    }
816
817    /// # Description
818    ///
819    /// -a
820    ///
821    /// For each file, print all revisions within a specified revision range,
822    /// rather than only the highest revision in the range.
823    pub fn set_all_revisions(&mut self, v: bool) -> &mut Self {
824        self.mode.all_revisions = v;
825        self
826    }
827
828    /// # Description
829    ///
830    /// -a
831    ///
832    /// For each file, print all revisions within a specified revision range,
833    /// rather than only the highest revision in the range.
834    pub fn all_revisions(mut self, v: bool) -> Self {
835        self.mode.all_revisions = v;
836        self
837    }
838
839    /// # Description
840    ///
841    /// -A
842    ///
843    #[cfg_attr(
844        feature = "lt2023_1",
845        doc = "Attempt to print a file stored in an archive depot."
846    )]
847    #[cfg_attr(not(feature = "lt2023_1"), doc = "Print files in archive depots.")]
848    pub fn get_archive_depots(&self) -> bool {
849        self.mode.from_archive_depots
850    }
851
852    /// # Description
853    ///
854    /// -A
855    ///
856    #[cfg_attr(
857        feature = "lt2023_1",
858        doc = "Attempt to print a file stored in an archive depot."
859    )]
860    #[cfg_attr(not(feature = "lt2023_1"), doc = "Print files in archive depots.")]
861    pub fn set_archive_depots(&mut self, v: bool) -> &mut Self {
862        self.mode.from_archive_depots = v;
863        self
864    }
865
866    /// # Description
867    ///
868    /// -A
869    ///
870    #[cfg_attr(
871        feature = "lt2023_1",
872        doc = "Attempt to print a file stored in an archive depot."
873    )]
874    #[cfg_attr(not(feature = "lt2023_1"), doc = "Print files in archive depots.")]
875    pub fn archive_depots(mut self, v: bool) -> Self {
876        self.mode.from_archive_depots = v;
877        self
878    }
879
880    /// # Description
881    ///
882    #[cfg_attr(feature = "lt2022_1", doc = "-k")]
883    #[cfg_attr(not(feature = "lt2022_1"), doc = "-K")]
884    ///
885    #[cfg_attr(feature = "lt2023_1", doc = "Suppress RCS keyword expansion.")]
886    #[cfg_attr(
887        not(feature = "lt2023_1"),
888        doc = "Suppress RCS keyword expansion. This replaced the `-k` flag in",
889        doc = "2022.1, which is now an alias for `-K` for backwards compatibility."
890    )]
891    pub fn get_suppress_keyword_expansion(&self) -> bool {
892        self.mode.suppress_keyword_expansion
893    }
894
895    /// # Description
896    ///
897    #[cfg_attr(feature = "lt2022_1", doc = "-k")]
898    #[cfg_attr(not(feature = "lt2022_1"), doc = "-K")]
899    ///
900    #[cfg_attr(feature = "lt2023_1", doc = "Suppress RCS keyword expansion.")]
901    #[cfg_attr(
902        not(feature = "lt2023_1"),
903        doc = "Suppress RCS keyword expansion. This replaced the `-k` flag in",
904        doc = "2022.1, which is now an alias for `-K` for backwards compatibility."
905    )]
906    pub fn set_suppress_keyword_expansion(&mut self, v: bool) -> &mut Self {
907        self.mode.suppress_keyword_expansion = v;
908        self
909    }
910
911    /// # Description
912    ///
913    #[cfg_attr(feature = "lt2022_1", doc = "-k")]
914    #[cfg_attr(not(feature = "lt2022_1"), doc = "-K")]
915    ///
916    #[cfg_attr(feature = "lt2023_1", doc = "Suppress RCS keyword expansion.")]
917    #[cfg_attr(
918        not(feature = "lt2023_1"),
919        doc = "Suppress RCS keyword expansion. This replaced the `-k` flag in",
920        doc = "2022.1, which is now an alias for `-K` for backwards compatibility."
921    )]
922    pub fn suppress_keyword_expansion(mut self, v: bool) -> Self {
923        self.mode.suppress_keyword_expansion = v;
924        self
925    }
926
927    /// # Description
928    ///
929    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
930    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
931    ///
932    #[cfg_attr(
933        feature = "lt2018_2",
934        doc = "Redirect output to the specified output file on the local disk,",
935        doc = "preserving the same file type, attributes, and/or permission bits",
936        doc = "as the original file in the depot."
937    )]
938    #[cfg_attr(
939        all(feature = "lt2023_1", not(feature = "lt2018_2")),
940        doc = "Redirect output to the specified output file (`outfile`) on the",
941        doc = "local disk. This preserves the same file type, attributes, and/or",
942        doc = "permission bits as the original file (`FileSpec`) in the depot.",
943        doc = "Multiple files can be written by using wildcards in the",
944        doc = "`localFile` argument that match wildcards in the depot",
945        doc = "(`FileSpec`) argument. For example: To print the contents of a",
946        doc = "directory and directories under that directory, use the `...`",
947        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
948        doc = "print all files that match readme.txt or readme.pdf, you might",
949        doc = "specify `p4 print -o readme.* //depot/readme.*`"
950    )]
951    #[cfg_attr(
952        not(feature = "lt2023_1"),
953        doc = "Redirect output to the specified output file (`localfile`) on the",
954        doc = "local disk. This preserves the same file type, attributes, and/or",
955        doc = "permission bits as the original file (`FileSpec`) in the depot.",
956        doc = "Multiple files can be written by using wildcards in the",
957        doc = "`localFile` argument that match wildcards in the depot",
958        doc = "(`FileSpec`) argument. For example: To print the contents of a",
959        doc = "directory and directories under that directory, use the `...`",
960        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
961        doc = "print all files that match readme.txt or readme.pdf, you might",
962        doc = "specify `p4 print -o readme.* //depot/readme.*`"
963    )]
964    pub fn get_redirect_output(&self) -> Option<&PathBuf> {
965        self.mode.redirect_output.as_ref()
966    }
967
968    /// # Description
969    ///
970    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
971    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
972    ///
973    #[cfg_attr(
974        feature = "lt2018_2",
975        doc = "Redirect output to the specified output file on the local disk,",
976        doc = "preserving the same file type, attributes, and/or permission bits",
977        doc = "as the original file in the depot."
978    )]
979    #[cfg_attr(
980        all(feature = "lt2023_1", not(feature = "lt2018_2")),
981        doc = "Redirect output to the specified output file (`outfile`) on the",
982        doc = "local disk. This preserves the same file type, attributes, and/or",
983        doc = "permission bits as the original file (`FileSpec`) in the depot.",
984        doc = "Multiple files can be written by using wildcards in the",
985        doc = "`localFile` argument that match wildcards in the depot",
986        doc = "(`FileSpec`) argument. For example: To print the contents of a",
987        doc = "directory and directories under that directory, use the `...`",
988        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
989        doc = "print all files that match readme.txt or readme.pdf, you might",
990        doc = "specify `p4 print -o readme.* //depot/readme.*`"
991    )]
992    #[cfg_attr(
993        not(feature = "lt2023_1"),
994        doc = "Redirect output to the specified output file (`localfile`) on the",
995        doc = "local disk. This preserves the same file type, attributes, and/or",
996        doc = "permission bits as the original file (`FileSpec`) in the depot.",
997        doc = "Multiple files can be written by using wildcards in the",
998        doc = "`localFile` argument that match wildcards in the depot",
999        doc = "(`FileSpec`) argument. For example: To print the contents of a",
1000        doc = "directory and directories under that directory, use the `...`",
1001        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
1002        doc = "print all files that match readme.txt or readme.pdf, you might",
1003        doc = "specify `p4 print -o readme.* //depot/readme.*`"
1004    )]
1005    pub fn set_redirect_output(&mut self, v: impl Into<PathBuf>) -> &mut Self {
1006        self.mode.redirect_output = Some(v.into());
1007        self
1008    }
1009
1010    /// # Description
1011    ///
1012    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1013    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1014    ///
1015    #[cfg_attr(
1016        feature = "lt2018_2",
1017        doc = "Redirect output to the specified output file on the local disk,",
1018        doc = "preserving the same file type, attributes, and/or permission bits",
1019        doc = "as the original file in the depot."
1020    )]
1021    #[cfg_attr(
1022        all(feature = "lt2023_1", not(feature = "lt2018_2")),
1023        doc = "Redirect output to the specified output file (`outfile`) on the",
1024        doc = "local disk. This preserves the same file type, attributes, and/or",
1025        doc = "permission bits as the original file (`FileSpec`) in the depot.",
1026        doc = "Multiple files can be written by using wildcards in the",
1027        doc = "`localFile` argument that match wildcards in the depot",
1028        doc = "(`FileSpec`) argument. For example: To print the contents of a",
1029        doc = "directory and directories under that directory, use the `...`",
1030        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
1031        doc = "print all files that match readme.txt or readme.pdf, you might",
1032        doc = "specify `p4 print -o readme.* //depot/readme.*`"
1033    )]
1034    #[cfg_attr(
1035        not(feature = "lt2023_1"),
1036        doc = "Redirect output to the specified output file (`localfile`) on the",
1037        doc = "local disk. This preserves the same file type, attributes, and/or",
1038        doc = "permission bits as the original file (`FileSpec`) in the depot.",
1039        doc = "Multiple files can be written by using wildcards in the",
1040        doc = "`localFile` argument that match wildcards in the depot",
1041        doc = "(`FileSpec`) argument. For example: To print the contents of a",
1042        doc = "directory and directories under that directory, use the `...`",
1043        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
1044        doc = "print all files that match readme.txt or readme.pdf, you might",
1045        doc = "specify `p4 print -o readme.* //depot/readme.*`"
1046    )]
1047    pub fn redirect_output(mut self, v: impl Into<PathBuf>) -> Self {
1048        self.mode.redirect_output = Some(v.into());
1049        self
1050    }
1051
1052    /// # Description
1053    ///
1054    /// -q
1055    ///
1056    /// Suppress the one-line file header normally added by
1057    #[cfg_attr(feature = "lt2018_1", doc = "Perforce.")]
1058    #[cfg_attr(
1059        all(feature = "lt2019_1", not(feature = "lt2018_1")),
1060        doc = "Helix Server."
1061    )]
1062    #[cfg_attr(
1063        all(feature = "lt2021_1", not(feature = "lt2019_1")),
1064        doc = "Helix server."
1065    )]
1066    #[cfg_attr(
1067        all(feature = "lt2024_1", not(feature = "lt2021_1")),
1068        doc = "Helix Server."
1069    )]
1070    #[cfg_attr(
1071        all(feature = "lt2024_2", not(feature = "lt2024_1")),
1072        doc = "Helix Core Server."
1073    )]
1074    #[cfg_attr(not(feature = "lt2024_2"), doc = "P4 Server.")]
1075    pub fn get_quiet_mode(&self) -> bool {
1076        self.mode.quiet_mode
1077    }
1078
1079    /// # Description
1080    ///
1081    /// -q
1082    ///
1083    /// Suppress the one-line file header normally added by
1084    #[cfg_attr(feature = "lt2018_1", doc = "Perforce.")]
1085    #[cfg_attr(
1086        all(feature = "lt2019_1", not(feature = "lt2018_1")),
1087        doc = "Helix Server."
1088    )]
1089    #[cfg_attr(
1090        all(feature = "lt2021_1", not(feature = "lt2019_1")),
1091        doc = "Helix server."
1092    )]
1093    #[cfg_attr(
1094        all(feature = "lt2024_1", not(feature = "lt2021_1")),
1095        doc = "Helix Server."
1096    )]
1097    #[cfg_attr(
1098        all(feature = "lt2024_2", not(feature = "lt2024_1")),
1099        doc = "Helix Core Server."
1100    )]
1101    #[cfg_attr(not(feature = "lt2024_2"), doc = "P4 Server.")]
1102    pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
1103        self.mode.quiet_mode = v;
1104        self
1105    }
1106
1107    /// # Description
1108    ///
1109    /// -q
1110    ///
1111    /// Suppress the one-line file header normally added by
1112    #[cfg_attr(feature = "lt2018_1", doc = "Perforce.")]
1113    #[cfg_attr(
1114        all(feature = "lt2019_1", not(feature = "lt2018_1")),
1115        doc = "Helix Server."
1116    )]
1117    #[cfg_attr(
1118        all(feature = "lt2021_1", not(feature = "lt2019_1")),
1119        doc = "Helix server."
1120    )]
1121    #[cfg_attr(
1122        all(feature = "lt2024_1", not(feature = "lt2021_1")),
1123        doc = "Helix Server."
1124    )]
1125    #[cfg_attr(
1126        all(feature = "lt2024_2", not(feature = "lt2024_1")),
1127        doc = "Helix Core Server."
1128    )]
1129    #[cfg_attr(not(feature = "lt2024_2"), doc = "P4 Server.")]
1130    pub fn quiet_mode(mut self, v: bool) -> Self {
1131        self.mode.quiet_mode = v;
1132        self
1133    }
1134
1135    /// # Description
1136    ///
1137    /// -m max
1138    ///
1139    /// Print only the first *max* files.
1140    pub fn get_limit(&self) -> Option<u64> {
1141        self.mode.limit
1142    }
1143
1144    /// # Description
1145    ///
1146    /// -m max
1147    ///
1148    /// Print only the first *max* files.
1149    pub fn set_limit(&mut self, v: u64) -> &mut Self {
1150        self.mode.limit = Some(v);
1151        self
1152    }
1153
1154    /// # Description
1155    ///
1156    /// -m max
1157    ///
1158    /// Print only the first *max* files.
1159    pub fn limit(mut self, v: u64) -> Self {
1160        self.mode.limit = Some(v);
1161        self
1162    }
1163
1164    /// # Description
1165    ///
1166    /// --offset bytesToSkip
1167    ///
1168    /// (Optional) Skip the specified number of bytes and only print what
1169    /// follows. Can be used with `--size`.
1170    #[cfg(not(feature = "lt2022_1"))]
1171    pub fn get_offset(&self) -> Option<u64> {
1172        self.mode.offset
1173    }
1174
1175    /// # Description
1176    ///
1177    /// --offset bytesToSkip
1178    ///
1179    /// (Optional) Skip the specified number of bytes and only print what
1180    /// follows. Can be used with `--size`.
1181    #[cfg(not(feature = "lt2022_1"))]
1182    pub fn set_offset(&mut self, v: u64) -> &mut Self {
1183        self.mode.offset = Some(v);
1184        self
1185    }
1186
1187    /// # Description
1188    ///
1189    /// --offset bytesToSkip
1190    ///
1191    /// (Optional) Skip the specified number of bytes and only print what
1192    /// follows. Can be used with `--size`.
1193    #[cfg(not(feature = "lt2022_1"))]
1194    pub fn offset(mut self, v: u64) -> Self {
1195        self.mode.offset = Some(v);
1196        self
1197    }
1198
1199    /// # Description
1200    ///
1201    /// --size bytesToPrint
1202    ///
1203    /// (Optional) Print the specified number of bytes from the offset. If
1204    /// `--offset` is not explicitly set, prints the specified number of bytes
1205    /// from the beginning of the file.
1206    #[cfg(not(feature = "lt2022_1"))]
1207    pub fn get_size(&self) -> Option<u64> {
1208        self.mode.size
1209    }
1210
1211    /// # Description
1212    ///
1213    /// --size bytesToPrint
1214    ///
1215    /// (Optional) Print the specified number of bytes from the offset. If
1216    /// `--offset` is not explicitly set, prints the specified number of bytes
1217    /// from the beginning of the file.
1218    #[cfg(not(feature = "lt2022_1"))]
1219    pub fn set_size(&mut self, v: u64) -> &mut Self {
1220        self.mode.size = Some(v);
1221        self
1222    }
1223
1224    /// # Description
1225    ///
1226    /// --size bytesToPrint
1227    ///
1228    /// (Optional) Print the specified number of bytes from the offset. If
1229    /// `--offset` is not explicitly set, prints the specified number of bytes
1230    /// from the beginning of the file.
1231    #[cfg(not(feature = "lt2022_1"))]
1232    pub fn size(mut self, v: u64) -> Self {
1233        self.mode.size = Some(v);
1234        self
1235    }
1236
1237    /// # Description
1238    ///
1239    /// -Q charset
1240    ///
1241    /// Allow the charset for unicode type files to be explicitly specified,
1242    /// overriding the connection's `P4CHARSET` but not overriding the charset
1243    /// of unicode files with versioned charsets.
1244    #[cfg(not(feature = "lt2023_1"))]
1245    pub fn get_charset(&self) -> Option<&str> {
1246        self.mode.charset.as_deref()
1247    }
1248
1249    /// # Description
1250    ///
1251    /// -Q charset
1252    ///
1253    /// Allow the charset for unicode type files to be explicitly specified,
1254    /// overriding the connection's `P4CHARSET` but not overriding the charset
1255    /// of unicode files with versioned charsets.
1256    #[cfg(not(feature = "lt2023_1"))]
1257    pub fn set_charset(&mut self, v: impl Into<String>) -> &mut Self {
1258        self.mode.charset = Some(v.into());
1259        self
1260    }
1261
1262    /// # Description
1263    ///
1264    /// -Q charset
1265    ///
1266    /// Allow the charset for unicode type files to be explicitly specified,
1267    /// overriding the connection's `P4CHARSET` but not overriding the charset
1268    /// of unicode files with versioned charsets.
1269    #[cfg(not(feature = "lt2023_1"))]
1270    pub fn charset(mut self, v: impl Into<String>) -> Self {
1271        self.mode.charset = Some(v.into());
1272        self
1273    }
1274
1275    /// # Description
1276    ///
1277    /// -B utf8bom
1278    ///
1279    /// Override the client-side `filesys.utf8bom` setting, controlling the
1280    /// presence of the byte-order-mark in utf8 type files.
1281    #[cfg(not(feature = "lt2023_1"))]
1282    pub fn get_utf8bom(&self) -> Option<Utf8Bom> {
1283        self.mode.utf8bom
1284    }
1285
1286    /// # Description
1287    ///
1288    /// -B 1 / -B 0
1289    ///
1290    /// Override the client-side `filesys.utf8bom` setting, controlling the
1291    /// presence of the byte-order-mark in utf8 type files. With `true`,
1292    /// write utf8 files with a BOM (`-B 1`); with `false`, do not write a
1293    /// BOM (`-B 0`).
1294    #[cfg(not(feature = "lt2023_1"))]
1295    pub fn set_write_utf8bom(&mut self, v: bool) -> &mut Self {
1296        self.mode.utf8bom = Some(if v { Utf8Bom::Yes } else { Utf8Bom::No });
1297        self
1298    }
1299
1300    /// # Description
1301    ///
1302    /// -B 1 / -B 0
1303    ///
1304    /// Override the client-side `filesys.utf8bom` setting, controlling the
1305    /// presence of the byte-order-mark in utf8 type files. With `true`,
1306    /// write utf8 files with a BOM (`-B 1`); with `false`, do not write a
1307    /// BOM (`-B 0`).
1308    #[cfg(not(feature = "lt2023_1"))]
1309    pub fn write_utf8bom(mut self, v: bool) -> Self {
1310        self.mode.utf8bom = Some(if v { Utf8Bom::Yes } else { Utf8Bom::No });
1311        self
1312    }
1313
1314    /// # Description
1315    ///
1316    /// -B 2
1317    ///
1318    /// Override the client-side `filesys.utf8bom` setting to write the BOM
1319    /// only on Windows.
1320    #[cfg(not(feature = "lt2023_1"))]
1321    pub fn set_write_utf8bom_windows_only(&mut self) -> &mut Self {
1322        self.mode.utf8bom = Some(Utf8Bom::WindowsOnly);
1323        self
1324    }
1325
1326    /// # Description
1327    ///
1328    /// -B 2
1329    ///
1330    /// Override the client-side `filesys.utf8bom` setting to write the BOM
1331    /// only on Windows.
1332    #[cfg(not(feature = "lt2023_1"))]
1333    pub fn write_utf8bom_windows_only(mut self) -> Self {
1334        self.mode.utf8bom = Some(Utf8Bom::WindowsOnly);
1335        self
1336    }
1337
1338    /// # Description
1339    ///
1340    /// -L line-ending
1341    ///
1342    /// Allow the line ending of textual files to be explicitly specified as
1343    /// 'unix', 'win', or 'mac'.
1344    ///
1345    /// Returns the line ending currently set, or `None` if no line ending is
1346    /// set.
1347    #[cfg(not(feature = "lt2023_1"))]
1348    pub fn get_line_ending(&self) -> Option<LineEnding> {
1349        self.mode.line_ending
1350    }
1351
1352    /// # Description
1353    ///
1354    /// -L unix
1355    ///
1356    /// Allow the line ending of textual files to be explicitly specified as
1357    /// 'unix', 'win', or 'mac'.
1358    #[cfg(not(feature = "lt2023_1"))]
1359    pub fn set_line_ending_unix(&mut self) -> &mut Self {
1360        self.mode.line_ending = Some(LineEnding::Unix);
1361        self
1362    }
1363
1364    /// # Description
1365    ///
1366    /// -L unix
1367    ///
1368    /// Allow the line ending of textual files to be explicitly specified as
1369    /// 'unix', 'win', or 'mac'.
1370    #[cfg(not(feature = "lt2023_1"))]
1371    pub fn line_ending_unix(mut self) -> Self {
1372        self.mode.line_ending = Some(LineEnding::Unix);
1373        self
1374    }
1375
1376    /// # Description
1377    ///
1378    /// -L win
1379    ///
1380    /// Allow the line ending of textual files to be explicitly specified as
1381    /// 'unix', 'win', or 'mac'.
1382    #[cfg(not(feature = "lt2023_1"))]
1383    pub fn set_line_ending_win(&mut self) -> &mut Self {
1384        self.mode.line_ending = Some(LineEnding::Win);
1385        self
1386    }
1387
1388    /// # Description
1389    ///
1390    /// -L win
1391    ///
1392    /// Allow the line ending of textual files to be explicitly specified as
1393    /// 'unix', 'win', or 'mac'.
1394    #[cfg(not(feature = "lt2023_1"))]
1395    pub fn line_ending_win(mut self) -> Self {
1396        self.mode.line_ending = Some(LineEnding::Win);
1397        self
1398    }
1399
1400    /// # Description
1401    ///
1402    /// -L mac
1403    ///
1404    /// Allow the line ending of textual files to be explicitly specified as
1405    /// 'unix', 'win', or 'mac'.
1406    #[cfg(not(feature = "lt2023_1"))]
1407    pub fn set_line_ending_mac(&mut self) -> &mut Self {
1408        self.mode.line_ending = Some(LineEnding::Mac);
1409        self
1410    }
1411
1412    /// # Description
1413    ///
1414    /// -L mac
1415    ///
1416    /// Allow the line ending of textual files to be explicitly specified as
1417    /// 'unix', 'win', or 'mac'.
1418    #[cfg(not(feature = "lt2023_1"))]
1419    pub fn line_ending_mac(mut self) -> Self {
1420        self.mode.line_ending = Some(LineEnding::Mac);
1421        self
1422    }
1423
1424    /// # Description
1425    ///
1426    /// --ignore-changeview
1427    ///
1428    /// Remove the changelist limit on depot paths. See ChangeView in
1429    /// `p4 client`.
1430    #[cfg(not(feature = "lt2026_1"))]
1431    pub fn get_ignore_changeview(&self) -> bool {
1432        self.mode.ignore_changeview
1433    }
1434
1435    /// # Description
1436    ///
1437    /// --ignore-changeview
1438    ///
1439    /// Remove the changelist limit on depot paths. See ChangeView in
1440    /// `p4 client`.
1441    #[cfg(not(feature = "lt2026_1"))]
1442    pub fn set_ignore_changeview(&mut self, v: bool) -> &mut Self {
1443        self.mode.ignore_changeview = v;
1444        self
1445    }
1446
1447    /// # Description
1448    ///
1449    /// --ignore-changeview
1450    ///
1451    /// Remove the changelist limit on depot paths. See ChangeView in
1452    /// `p4 client`.
1453    #[cfg(not(feature = "lt2026_1"))]
1454    pub fn ignore_changeview(mut self, v: bool) -> Self {
1455        self.mode.ignore_changeview = v;
1456        self
1457    }
1458}
1459
1460#[cfg(not(feature = "lt2024_2"))]
1461impl Print<AttributeTraitMode> {
1462    /// # Description
1463    ///
1464    /// -T attribute
1465    ///
1466    /// Print the value of the specified non-encoded attribute of the specified
1467    /// file.
1468    pub fn get_attribute(&self) -> &str {
1469        &self.mode.attribute
1470    }
1471
1472    /// # Description
1473    ///
1474    /// -T attribute
1475    ///
1476    /// Print the value of the specified non-encoded attribute of the specified
1477    /// file.
1478    pub fn set_attribute(&mut self, v: impl Into<String>) -> &mut Self {
1479        self.mode.attribute = v.into();
1480        self
1481    }
1482
1483    /// # Description
1484    ///
1485    /// -T attribute
1486    ///
1487    /// Print the value of the specified non-encoded attribute of the specified
1488    /// file.
1489    pub fn attribute(mut self, v: impl Into<String>) -> Self {
1490        self.mode.attribute = v.into();
1491        self
1492    }
1493
1494    /// # Description
1495    ///
1496    /// -a
1497    ///
1498    /// For each file, print all revisions within a specified revision range,
1499    /// rather than only the highest revision in the range.
1500    pub fn get_all_revisions(&self) -> bool {
1501        self.mode.all_revisions
1502    }
1503
1504    /// # Description
1505    ///
1506    /// -a
1507    ///
1508    /// For each file, print all revisions within a specified revision range,
1509    /// rather than only the highest revision in the range.
1510    pub fn set_all_revisions(&mut self, v: bool) -> &mut Self {
1511        self.mode.all_revisions = v;
1512        self
1513    }
1514
1515    /// # Description
1516    ///
1517    /// -a
1518    ///
1519    /// For each file, print all revisions within a specified revision range,
1520    /// rather than only the highest revision in the range.
1521    pub fn all_revisions(mut self, v: bool) -> Self {
1522        self.mode.all_revisions = v;
1523        self
1524    }
1525
1526    /// # Description
1527    ///
1528    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1529    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1530    ///
1531    /// Redirect output to the specified output file on the local disk,
1532    /// preserving the same file type, attributes, and/or permission bits as
1533    /// the original file in the depot.
1534    pub fn get_redirect_output(&self) -> Option<&PathBuf> {
1535        self.mode.redirect_output.as_ref()
1536    }
1537
1538    /// # Description
1539    ///
1540    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1541    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1542    ///
1543    /// Redirect output to the specified output file on the local disk,
1544    /// preserving the same file type, attributes, and/or permission bits as
1545    /// the original file in the depot.
1546    pub fn set_redirect_output(&mut self, v: impl Into<PathBuf>) -> &mut Self {
1547        self.mode.redirect_output = Some(v.into());
1548        self
1549    }
1550
1551    /// # Description
1552    ///
1553    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1554    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1555    ///
1556    /// Redirect output to the specified output file on the local disk,
1557    /// preserving the same file type, attributes, and/or permission bits as
1558    /// the original file in the depot.
1559    pub fn redirect_output(mut self, v: impl Into<PathBuf>) -> Self {
1560        self.mode.redirect_output = Some(v.into());
1561        self
1562    }
1563
1564    /// # Description
1565    ///
1566    /// -q
1567    ///
1568    /// Suppress the one-line file header normally added by the Helix Core
1569    /// Server.
1570    pub fn get_quiet_mode(&self) -> bool {
1571        self.mode.quiet_mode
1572    }
1573
1574    /// # Description
1575    ///
1576    /// -q
1577    ///
1578    /// Suppress the one-line file header normally added by the Helix Core
1579    /// Server.
1580    pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
1581        self.mode.quiet_mode = v;
1582        self
1583    }
1584
1585    /// # Description
1586    ///
1587    /// -q
1588    ///
1589    /// Suppress the one-line file header normally added by the Helix Core
1590    /// Server.
1591    pub fn quiet_mode(mut self, v: bool) -> Self {
1592        self.mode.quiet_mode = v;
1593        self
1594    }
1595
1596    /// # Description
1597    ///
1598    /// --ignore-changeview
1599    ///
1600    /// Remove the changelist limit on depot paths. See ChangeView in
1601    /// `p4 client`.
1602    #[cfg(not(feature = "lt2026_1"))]
1603    pub fn get_ignore_changeview(&self) -> bool {
1604        self.mode.ignore_changeview
1605    }
1606
1607    /// # Description
1608    ///
1609    /// --ignore-changeview
1610    ///
1611    /// Remove the changelist limit on depot paths. See ChangeView in
1612    /// `p4 client`.
1613    #[cfg(not(feature = "lt2026_1"))]
1614    pub fn set_ignore_changeview(&mut self, v: bool) -> &mut Self {
1615        self.mode.ignore_changeview = v;
1616        self
1617    }
1618
1619    /// # Description
1620    ///
1621    /// --ignore-changeview
1622    ///
1623    /// Remove the changelist limit on depot paths. See ChangeView in
1624    /// `p4 client`.
1625    #[cfg(not(feature = "lt2026_1"))]
1626    pub fn ignore_changeview(mut self, v: bool) -> Self {
1627        self.mode.ignore_changeview = v;
1628        self
1629    }
1630}
1631
1632impl<M: ExclusiveOption> SubCommand for Print<M> {
1633    fn name(&self) -> &str {
1634        "print"
1635    }
1636
1637    fn inject_local_args(&self, command: &mut Command) {
1638        self.mode.inject_args(command);
1639    }
1640
1641    fn global_opts(&self) -> Option<&GlobalOpts> {
1642        Some(&self.global_opts)
1643    }
1644}
1645
1646#[cfg(test)]
1647mod tests {
1648    use super::*;
1649    use crate::cmd::args_of;
1650
1651    /// Dry-run checks of the assembled `p4 print` command line; no process is
1652    /// spawned.
1653    #[test]
1654    fn without_options() {
1655        let print = Print::new("p4", GlobalOpts::new());
1656
1657        assert_eq!(args_of(&print.setup_command("p4")), ["print"]);
1658    }
1659
1660    #[test]
1661    fn standard_mode_all_options() {
1662        let print = Print::new("p4", GlobalOpts::new())
1663            .all_revisions(true)
1664            .archive_depots(true)
1665            .suppress_keyword_expansion(true)
1666            .redirect_output("out.bin")
1667            .quiet_mode(true)
1668            .limit(5);
1669        #[cfg(not(feature = "lt2022_1"))]
1670        let print = print.offset(100).size(200);
1671        #[cfg(not(feature = "lt2023_1"))]
1672        let print = print.charset("utf8").write_utf8bom(true).line_ending_unix();
1673        #[cfg(not(feature = "lt2026_1"))]
1674        let print = print.ignore_changeview(true);
1675
1676        let mut expected = vec!["print", "-a", "-A"];
1677        #[cfg(feature = "lt2022_1")]
1678        expected.push("-k");
1679        #[cfg(not(feature = "lt2022_1"))]
1680        expected.push("-K");
1681        expected.extend(["-o", "out.bin", "-q", "-m", "5"]);
1682        #[cfg(not(feature = "lt2022_1"))]
1683        expected.extend(["--offset", "100", "--size", "200"]);
1684        #[cfg(not(feature = "lt2023_1"))]
1685        expected.extend(["-Q", "utf8", "-B", "1", "-L", "unix"]);
1686        #[cfg(not(feature = "lt2026_1"))]
1687        expected.push("--ignore-changeview");
1688
1689        assert_eq!(args_of(&print.setup_command("p4")), expected);
1690    }
1691
1692    #[test]
1693    fn standard_mode_set_style() {
1694        let mut print = Print::new("p4", GlobalOpts::new()).limit(5);
1695        print.set_all_revisions(true).set_quiet_mode(true);
1696        #[cfg(not(feature = "lt2022_1"))]
1697        print.set_offset(100).set_size(200);
1698
1699        let expected = {
1700            #[cfg_attr(feature = "lt2022_1", allow(unused_mut))]
1701            let mut v = vec!["print", "-a", "-q", "-m", "5"];
1702            #[cfg(not(feature = "lt2022_1"))]
1703            v.extend(["--offset", "100", "--size", "200"]);
1704            v
1705        };
1706
1707        assert_eq!(args_of(&print.setup_command("p4")), expected);
1708    }
1709
1710    #[test]
1711    fn unload_depot_mode() {
1712        let print = Print::new("p4", GlobalOpts::new()).from_unload_depot();
1713
1714        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-U"]);
1715    }
1716
1717    #[cfg(not(feature = "lt2023_1"))]
1718    #[test]
1719    fn line_ending_is_passed_as_separate_args() {
1720        let print = Print::new("p4", GlobalOpts::new()).line_ending_win();
1721
1722        assert_eq!(print.get_line_ending(), Some(LineEnding::Win));
1723        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-L", "win"]);
1724    }
1725
1726    #[cfg(not(feature = "lt2023_1"))]
1727    #[test]
1728    fn write_utf8bom_bool() {
1729        let yes = Print::new("p4", GlobalOpts::new()).write_utf8bom(true);
1730        assert_eq!(yes.get_utf8bom(), Some(Utf8Bom::Yes));
1731        assert_eq!(args_of(&yes.setup_command("p4")), ["print", "-B", "1"]);
1732
1733        let no = Print::new("p4", GlobalOpts::new()).write_utf8bom(false);
1734        assert_eq!(no.get_utf8bom(), Some(Utf8Bom::No));
1735        assert_eq!(args_of(&no.setup_command("p4")), ["print", "-B", "0"]);
1736    }
1737
1738    #[cfg(not(feature = "lt2023_1"))]
1739    #[test]
1740    fn write_utf8bom_windows_only() {
1741        let print = Print::new("p4", GlobalOpts::new()).write_utf8bom_windows_only();
1742
1743        assert_eq!(print.get_utf8bom(), Some(Utf8Bom::WindowsOnly));
1744        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-B", "2"]);
1745    }
1746
1747    #[cfg(not(feature = "lt2023_1"))]
1748    #[test]
1749    fn utf8bom_set_style_can_replace() {
1750        let mut print = Print::new("p4", GlobalOpts::new()).write_utf8bom(true);
1751        print.set_write_utf8bom(false);
1752        assert_eq!(print.get_utf8bom(), Some(Utf8Bom::No));
1753
1754        print.set_write_utf8bom_windows_only();
1755        assert_eq!(print.get_utf8bom(), Some(Utf8Bom::WindowsOnly));
1756        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-B", "2"]);
1757    }
1758
1759    #[cfg(not(feature = "lt2024_2"))]
1760    #[test]
1761    fn attribute_trait_mode() {
1762        let print = Print::new("p4", GlobalOpts::new()).extract_attribute("desc");
1763
1764        assert_eq!(print.get_attribute(), "desc");
1765        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-T", "desc"]);
1766    }
1767
1768    #[cfg(not(feature = "lt2024_2"))]
1769    #[test]
1770    fn attribute_trait_mode_with_allowed_options() {
1771        let print = Print::new("p4", GlobalOpts::new())
1772            .extract_attribute("desc")
1773            .all_revisions(true)
1774            .quiet_mode(true)
1775            .redirect_output("out.bin");
1776        #[cfg(not(feature = "lt2026_1"))]
1777        let print = print.ignore_changeview(true);
1778
1779        let mut expected = vec!["print", "-T", "desc", "-a", "-o", "out.bin", "-q"];
1780        #[cfg(not(feature = "lt2026_1"))]
1781        expected.push("--ignore-changeview");
1782
1783        assert_eq!(args_of(&print.setup_command("p4")), expected);
1784    }
1785
1786    #[cfg(not(feature = "lt2024_2"))]
1787    #[test]
1788    fn attribute_name_can_be_replaced() {
1789        let mut print = Print::new("p4", GlobalOpts::new()).extract_attribute("old");
1790        print.set_attribute("new");
1791
1792        assert_eq!(print.get_attribute(), "new");
1793        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-T", "new"]);
1794    }
1795
1796    #[cfg(not(feature = "lt2026_1"))]
1797    #[test]
1798    fn ignore_changeview_standard_mode() {
1799        let print = Print::new("p4", GlobalOpts::new()).ignore_changeview(true);
1800
1801        assert!(print.get_ignore_changeview());
1802        assert_eq!(
1803            args_of(&print.setup_command("p4")),
1804            ["print", "--ignore-changeview"]
1805        );
1806    }
1807}