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> ParameterizedSpawn for Print<M> {
708    type Input<'a> = &'a [&'a OsStr];
709    type Output<'a> = Child;
710    type Error = std::io::Error;
711
712    /// Spawns `p4 print` for the given files as a child process with piped
713    /// standard output and error streams; use the returned [`Child`] handle
714    /// to wait for it or interact with it.
715    fn spawn_with<'a>(&mut self, files: Self::Input<'a>) -> Result<Self::Output<'a>, Self::Error> {
716        self.setup_command(&self.bin)
717            .args(files)
718            .stdout(Stdio::piped())
719            .stderr(Stdio::piped())
720            .spawn()
721    }
722}
723
724impl<M: ExclusiveOption> Print<M> {
725    /// # Description
726    ///
727    /// g-opts
728    ///
729    #[cfg_attr(
730        feature = "lt2014_2",
731        doc = "See the [Global Options](GlobalOpts) section."
732    )]
733    #[cfg_attr(
734        all(feature = "lt2015_1", not(feature = "lt2014_2")),
735        doc = "See the [“Global Options”](GlobalOpts) section."
736    )]
737    #[cfg_attr(
738        all(feature = "lt2017_1", not(feature = "lt2015_1")),
739        doc = "See [“Global Options”](GlobalOpts)."
740    )]
741    #[cfg_attr(
742        all(feature = "lt2018_2", not(feature = "lt2017_1")),
743        doc = "See [Global Options](GlobalOpts)."
744    )]
745    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
746    pub fn get_global_opts(&self) -> &GlobalOpts {
747        &self.global_opts
748    }
749
750    /// # Description
751    ///
752    /// g-opts
753    ///
754    #[cfg_attr(
755        feature = "lt2014_2",
756        doc = "See the [Global Options](GlobalOpts) section."
757    )]
758    #[cfg_attr(
759        all(feature = "lt2015_1", not(feature = "lt2014_2")),
760        doc = "See the [“Global Options”](GlobalOpts) section."
761    )]
762    #[cfg_attr(
763        all(feature = "lt2017_1", not(feature = "lt2015_1")),
764        doc = "See [“Global Options”](GlobalOpts)."
765    )]
766    #[cfg_attr(
767        all(feature = "lt2018_2", not(feature = "lt2017_1")),
768        doc = "See [Global Options](GlobalOpts)."
769    )]
770    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
771    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
772        self.global_opts = v;
773        self
774    }
775
776    /// # Description
777    ///
778    /// g-opts
779    ///
780    #[cfg_attr(
781        feature = "lt2014_2",
782        doc = "See the [Global Options](GlobalOpts) section."
783    )]
784    #[cfg_attr(
785        all(feature = "lt2015_1", not(feature = "lt2014_2")),
786        doc = "See the [“Global Options”](GlobalOpts) section."
787    )]
788    #[cfg_attr(
789        all(feature = "lt2017_1", not(feature = "lt2015_1")),
790        doc = "See [“Global Options”](GlobalOpts)."
791    )]
792    #[cfg_attr(
793        all(feature = "lt2018_2", not(feature = "lt2017_1")),
794        doc = "See [Global Options](GlobalOpts)."
795    )]
796    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
797    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
798        self.global_opts = v;
799        self
800    }
801}
802
803impl Print<StandardPrintMode> {
804    /// # Description
805    ///
806    /// -a
807    ///
808    /// For each file, print all revisions within a specified revision range,
809    /// rather than only the highest revision in the range.
810    pub fn get_all_revisions(&self) -> bool {
811        self.mode.all_revisions
812    }
813
814    /// # Description
815    ///
816    /// -a
817    ///
818    /// For each file, print all revisions within a specified revision range,
819    /// rather than only the highest revision in the range.
820    pub fn set_all_revisions(&mut self, v: bool) -> &mut Self {
821        self.mode.all_revisions = v;
822        self
823    }
824
825    /// # Description
826    ///
827    /// -a
828    ///
829    /// For each file, print all revisions within a specified revision range,
830    /// rather than only the highest revision in the range.
831    pub fn all_revisions(mut self, v: bool) -> Self {
832        self.mode.all_revisions = v;
833        self
834    }
835
836    /// # Description
837    ///
838    /// -A
839    ///
840    #[cfg_attr(
841        feature = "lt2023_1",
842        doc = "Attempt to print a file stored in an archive depot."
843    )]
844    #[cfg_attr(not(feature = "lt2023_1"), doc = "Print files in archive depots.")]
845    pub fn get_archive_depots(&self) -> bool {
846        self.mode.from_archive_depots
847    }
848
849    /// # Description
850    ///
851    /// -A
852    ///
853    #[cfg_attr(
854        feature = "lt2023_1",
855        doc = "Attempt to print a file stored in an archive depot."
856    )]
857    #[cfg_attr(not(feature = "lt2023_1"), doc = "Print files in archive depots.")]
858    pub fn set_archive_depots(&mut self, v: bool) -> &mut Self {
859        self.mode.from_archive_depots = v;
860        self
861    }
862
863    /// # Description
864    ///
865    /// -A
866    ///
867    #[cfg_attr(
868        feature = "lt2023_1",
869        doc = "Attempt to print a file stored in an archive depot."
870    )]
871    #[cfg_attr(not(feature = "lt2023_1"), doc = "Print files in archive depots.")]
872    pub fn archive_depots(mut self, v: bool) -> Self {
873        self.mode.from_archive_depots = v;
874        self
875    }
876
877    /// # Description
878    ///
879    #[cfg_attr(feature = "lt2022_1", doc = "-k")]
880    #[cfg_attr(not(feature = "lt2022_1"), doc = "-K")]
881    ///
882    #[cfg_attr(feature = "lt2023_1", doc = "Suppress RCS keyword expansion.")]
883    #[cfg_attr(
884        not(feature = "lt2023_1"),
885        doc = "Suppress RCS keyword expansion. This replaced the `-k` flag in",
886        doc = "2022.1, which is now an alias for `-K` for backwards compatibility."
887    )]
888    pub fn get_suppress_keyword_expansion(&self) -> bool {
889        self.mode.suppress_keyword_expansion
890    }
891
892    /// # Description
893    ///
894    #[cfg_attr(feature = "lt2022_1", doc = "-k")]
895    #[cfg_attr(not(feature = "lt2022_1"), doc = "-K")]
896    ///
897    #[cfg_attr(feature = "lt2023_1", doc = "Suppress RCS keyword expansion.")]
898    #[cfg_attr(
899        not(feature = "lt2023_1"),
900        doc = "Suppress RCS keyword expansion. This replaced the `-k` flag in",
901        doc = "2022.1, which is now an alias for `-K` for backwards compatibility."
902    )]
903    pub fn set_suppress_keyword_expansion(&mut self, v: bool) -> &mut Self {
904        self.mode.suppress_keyword_expansion = v;
905        self
906    }
907
908    /// # Description
909    ///
910    #[cfg_attr(feature = "lt2022_1", doc = "-k")]
911    #[cfg_attr(not(feature = "lt2022_1"), doc = "-K")]
912    ///
913    #[cfg_attr(feature = "lt2023_1", doc = "Suppress RCS keyword expansion.")]
914    #[cfg_attr(
915        not(feature = "lt2023_1"),
916        doc = "Suppress RCS keyword expansion. This replaced the `-k` flag in",
917        doc = "2022.1, which is now an alias for `-K` for backwards compatibility."
918    )]
919    pub fn suppress_keyword_expansion(mut self, v: bool) -> Self {
920        self.mode.suppress_keyword_expansion = v;
921        self
922    }
923
924    /// # Description
925    ///
926    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
927    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
928    ///
929    #[cfg_attr(
930        feature = "lt2018_2",
931        doc = "Redirect output to the specified output file on the local disk,",
932        doc = "preserving the same file type, attributes, and/or permission bits",
933        doc = "as the original file in the depot."
934    )]
935    #[cfg_attr(
936        all(feature = "lt2023_1", not(feature = "lt2018_2")),
937        doc = "Redirect output to the specified output file (`outfile`) on the",
938        doc = "local disk. This preserves the same file type, attributes, and/or",
939        doc = "permission bits as the original file (`FileSpec`) in the depot.",
940        doc = "Multiple files can be written by using wildcards in the",
941        doc = "`localFile` argument that match wildcards in the depot",
942        doc = "(`FileSpec`) argument. For example: To print the contents of a",
943        doc = "directory and directories under that directory, use the `...`",
944        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
945        doc = "print all files that match readme.txt or readme.pdf, you might",
946        doc = "specify `p4 print -o readme.* //depot/readme.*`"
947    )]
948    #[cfg_attr(
949        not(feature = "lt2023_1"),
950        doc = "Redirect output to the specified output file (`localfile`) on the",
951        doc = "local disk. This preserves the same file type, attributes, and/or",
952        doc = "permission bits as the original file (`FileSpec`) in the depot.",
953        doc = "Multiple files can be written by using wildcards in the",
954        doc = "`localFile` argument that match wildcards in the depot",
955        doc = "(`FileSpec`) argument. For example: To print the contents of a",
956        doc = "directory and directories under that directory, use the `...`",
957        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
958        doc = "print all files that match readme.txt or readme.pdf, you might",
959        doc = "specify `p4 print -o readme.* //depot/readme.*`"
960    )]
961    pub fn get_redirect_output(&self) -> Option<&PathBuf> {
962        self.mode.redirect_output.as_ref()
963    }
964
965    /// # Description
966    ///
967    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
968    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
969    ///
970    #[cfg_attr(
971        feature = "lt2018_2",
972        doc = "Redirect output to the specified output file on the local disk,",
973        doc = "preserving the same file type, attributes, and/or permission bits",
974        doc = "as the original file in the depot."
975    )]
976    #[cfg_attr(
977        all(feature = "lt2023_1", not(feature = "lt2018_2")),
978        doc = "Redirect output to the specified output file (`outfile`) on the",
979        doc = "local disk. This preserves the same file type, attributes, and/or",
980        doc = "permission bits as the original file (`FileSpec`) in the depot.",
981        doc = "Multiple files can be written by using wildcards in the",
982        doc = "`localFile` argument that match wildcards in the depot",
983        doc = "(`FileSpec`) argument. For example: To print the contents of a",
984        doc = "directory and directories under that directory, use the `...`",
985        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
986        doc = "print all files that match readme.txt or readme.pdf, you might",
987        doc = "specify `p4 print -o readme.* //depot/readme.*`"
988    )]
989    #[cfg_attr(
990        not(feature = "lt2023_1"),
991        doc = "Redirect output to the specified output file (`localfile`) on the",
992        doc = "local disk. This preserves the same file type, attributes, and/or",
993        doc = "permission bits as the original file (`FileSpec`) in the depot.",
994        doc = "Multiple files can be written by using wildcards in the",
995        doc = "`localFile` argument that match wildcards in the depot",
996        doc = "(`FileSpec`) argument. For example: To print the contents of a",
997        doc = "directory and directories under that directory, use the `...`",
998        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
999        doc = "print all files that match readme.txt or readme.pdf, you might",
1000        doc = "specify `p4 print -o readme.* //depot/readme.*`"
1001    )]
1002    pub fn set_redirect_output(&mut self, v: impl Into<PathBuf>) -> &mut Self {
1003        self.mode.redirect_output = Some(v.into());
1004        self
1005    }
1006
1007    /// # Description
1008    ///
1009    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1010    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1011    ///
1012    #[cfg_attr(
1013        feature = "lt2018_2",
1014        doc = "Redirect output to the specified output file on the local disk,",
1015        doc = "preserving the same file type, attributes, and/or permission bits",
1016        doc = "as the original file in the depot."
1017    )]
1018    #[cfg_attr(
1019        all(feature = "lt2023_1", not(feature = "lt2018_2")),
1020        doc = "Redirect output to the specified output file (`outfile`) on the",
1021        doc = "local disk. This preserves the same file type, attributes, and/or",
1022        doc = "permission bits as the original file (`FileSpec`) in the depot.",
1023        doc = "Multiple files can be written by using wildcards in the",
1024        doc = "`localFile` argument that match wildcards in the depot",
1025        doc = "(`FileSpec`) argument. For example: To print the contents of a",
1026        doc = "directory and directories under that directory, use the `...`",
1027        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
1028        doc = "print all files that match readme.txt or readme.pdf, you might",
1029        doc = "specify `p4 print -o readme.* //depot/readme.*`"
1030    )]
1031    #[cfg_attr(
1032        not(feature = "lt2023_1"),
1033        doc = "Redirect output to the specified output file (`localfile`) on the",
1034        doc = "local disk. This preserves the same file type, attributes, and/or",
1035        doc = "permission bits as the original file (`FileSpec`) in the depot.",
1036        doc = "Multiple files can be written by using wildcards in the",
1037        doc = "`localFile` argument that match wildcards in the depot",
1038        doc = "(`FileSpec`) argument. For example: To print the contents of a",
1039        doc = "directory and directories under that directory, use the `...`",
1040        doc = "wildcard: `p4 print -o c:/tmp/main-copy/... //depot/main/...` To",
1041        doc = "print all files that match readme.txt or readme.pdf, you might",
1042        doc = "specify `p4 print -o readme.* //depot/readme.*`"
1043    )]
1044    pub fn redirect_output(mut self, v: impl Into<PathBuf>) -> Self {
1045        self.mode.redirect_output = Some(v.into());
1046        self
1047    }
1048
1049    /// # Description
1050    ///
1051    /// -q
1052    ///
1053    /// Suppress the one-line file header normally added by
1054    #[cfg_attr(feature = "lt2018_1", doc = "Perforce.")]
1055    #[cfg_attr(
1056        all(feature = "lt2019_1", not(feature = "lt2018_1")),
1057        doc = "Helix Server."
1058    )]
1059    #[cfg_attr(
1060        all(feature = "lt2021_1", not(feature = "lt2019_1")),
1061        doc = "Helix server."
1062    )]
1063    #[cfg_attr(
1064        all(feature = "lt2024_1", not(feature = "lt2021_1")),
1065        doc = "Helix Server."
1066    )]
1067    #[cfg_attr(
1068        all(feature = "lt2024_2", not(feature = "lt2024_1")),
1069        doc = "Helix Core Server."
1070    )]
1071    #[cfg_attr(not(feature = "lt2024_2"), doc = "P4 Server.")]
1072    pub fn get_quiet_mode(&self) -> bool {
1073        self.mode.quiet_mode
1074    }
1075
1076    /// # Description
1077    ///
1078    /// -q
1079    ///
1080    /// Suppress the one-line file header normally added by
1081    #[cfg_attr(feature = "lt2018_1", doc = "Perforce.")]
1082    #[cfg_attr(
1083        all(feature = "lt2019_1", not(feature = "lt2018_1")),
1084        doc = "Helix Server."
1085    )]
1086    #[cfg_attr(
1087        all(feature = "lt2021_1", not(feature = "lt2019_1")),
1088        doc = "Helix server."
1089    )]
1090    #[cfg_attr(
1091        all(feature = "lt2024_1", not(feature = "lt2021_1")),
1092        doc = "Helix Server."
1093    )]
1094    #[cfg_attr(
1095        all(feature = "lt2024_2", not(feature = "lt2024_1")),
1096        doc = "Helix Core Server."
1097    )]
1098    #[cfg_attr(not(feature = "lt2024_2"), doc = "P4 Server.")]
1099    pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
1100        self.mode.quiet_mode = v;
1101        self
1102    }
1103
1104    /// # Description
1105    ///
1106    /// -q
1107    ///
1108    /// Suppress the one-line file header normally added by
1109    #[cfg_attr(feature = "lt2018_1", doc = "Perforce.")]
1110    #[cfg_attr(
1111        all(feature = "lt2019_1", not(feature = "lt2018_1")),
1112        doc = "Helix Server."
1113    )]
1114    #[cfg_attr(
1115        all(feature = "lt2021_1", not(feature = "lt2019_1")),
1116        doc = "Helix server."
1117    )]
1118    #[cfg_attr(
1119        all(feature = "lt2024_1", not(feature = "lt2021_1")),
1120        doc = "Helix Server."
1121    )]
1122    #[cfg_attr(
1123        all(feature = "lt2024_2", not(feature = "lt2024_1")),
1124        doc = "Helix Core Server."
1125    )]
1126    #[cfg_attr(not(feature = "lt2024_2"), doc = "P4 Server.")]
1127    pub fn quiet_mode(mut self, v: bool) -> Self {
1128        self.mode.quiet_mode = v;
1129        self
1130    }
1131
1132    /// # Description
1133    ///
1134    /// -m max
1135    ///
1136    /// Print only the first *max* files.
1137    pub fn get_limit(&self) -> Option<u64> {
1138        self.mode.limit
1139    }
1140
1141    /// # Description
1142    ///
1143    /// -m max
1144    ///
1145    /// Print only the first *max* files.
1146    pub fn set_limit(&mut self, v: u64) -> &mut Self {
1147        self.mode.limit = Some(v);
1148        self
1149    }
1150
1151    /// # Description
1152    ///
1153    /// -m max
1154    ///
1155    /// Print only the first *max* files.
1156    pub fn limit(mut self, v: u64) -> Self {
1157        self.mode.limit = Some(v);
1158        self
1159    }
1160
1161    /// # Description
1162    ///
1163    /// --offset bytesToSkip
1164    ///
1165    /// (Optional) Skip the specified number of bytes and only print what
1166    /// follows. Can be used with `--size`.
1167    #[cfg(not(feature = "lt2022_1"))]
1168    pub fn get_offset(&self) -> Option<u64> {
1169        self.mode.offset
1170    }
1171
1172    /// # Description
1173    ///
1174    /// --offset bytesToSkip
1175    ///
1176    /// (Optional) Skip the specified number of bytes and only print what
1177    /// follows. Can be used with `--size`.
1178    #[cfg(not(feature = "lt2022_1"))]
1179    pub fn set_offset(&mut self, v: u64) -> &mut Self {
1180        self.mode.offset = Some(v);
1181        self
1182    }
1183
1184    /// # Description
1185    ///
1186    /// --offset bytesToSkip
1187    ///
1188    /// (Optional) Skip the specified number of bytes and only print what
1189    /// follows. Can be used with `--size`.
1190    #[cfg(not(feature = "lt2022_1"))]
1191    pub fn offset(mut self, v: u64) -> Self {
1192        self.mode.offset = Some(v);
1193        self
1194    }
1195
1196    /// # Description
1197    ///
1198    /// --size bytesToPrint
1199    ///
1200    /// (Optional) Print the specified number of bytes from the offset. If
1201    /// `--offset` is not explicitly set, prints the specified number of bytes
1202    /// from the beginning of the file.
1203    #[cfg(not(feature = "lt2022_1"))]
1204    pub fn get_size(&self) -> Option<u64> {
1205        self.mode.size
1206    }
1207
1208    /// # Description
1209    ///
1210    /// --size bytesToPrint
1211    ///
1212    /// (Optional) Print the specified number of bytes from the offset. If
1213    /// `--offset` is not explicitly set, prints the specified number of bytes
1214    /// from the beginning of the file.
1215    #[cfg(not(feature = "lt2022_1"))]
1216    pub fn set_size(&mut self, v: u64) -> &mut Self {
1217        self.mode.size = Some(v);
1218        self
1219    }
1220
1221    /// # Description
1222    ///
1223    /// --size bytesToPrint
1224    ///
1225    /// (Optional) Print the specified number of bytes from the offset. If
1226    /// `--offset` is not explicitly set, prints the specified number of bytes
1227    /// from the beginning of the file.
1228    #[cfg(not(feature = "lt2022_1"))]
1229    pub fn size(mut self, v: u64) -> Self {
1230        self.mode.size = Some(v);
1231        self
1232    }
1233
1234    /// # Description
1235    ///
1236    /// -Q charset
1237    ///
1238    /// Allow the charset for unicode type files to be explicitly specified,
1239    /// overriding the connection's `P4CHARSET` but not overriding the charset
1240    /// of unicode files with versioned charsets.
1241    #[cfg(not(feature = "lt2023_1"))]
1242    pub fn get_charset(&self) -> Option<&str> {
1243        self.mode.charset.as_deref()
1244    }
1245
1246    /// # Description
1247    ///
1248    /// -Q charset
1249    ///
1250    /// Allow the charset for unicode type files to be explicitly specified,
1251    /// overriding the connection's `P4CHARSET` but not overriding the charset
1252    /// of unicode files with versioned charsets.
1253    #[cfg(not(feature = "lt2023_1"))]
1254    pub fn set_charset(&mut self, v: impl Into<String>) -> &mut Self {
1255        self.mode.charset = Some(v.into());
1256        self
1257    }
1258
1259    /// # Description
1260    ///
1261    /// -Q charset
1262    ///
1263    /// Allow the charset for unicode type files to be explicitly specified,
1264    /// overriding the connection's `P4CHARSET` but not overriding the charset
1265    /// of unicode files with versioned charsets.
1266    #[cfg(not(feature = "lt2023_1"))]
1267    pub fn charset(mut self, v: impl Into<String>) -> Self {
1268        self.mode.charset = Some(v.into());
1269        self
1270    }
1271
1272    /// # Description
1273    ///
1274    /// -B utf8bom
1275    ///
1276    /// Override the client-side `filesys.utf8bom` setting, controlling the
1277    /// presence of the byte-order-mark in utf8 type files.
1278    #[cfg(not(feature = "lt2023_1"))]
1279    pub fn get_utf8bom(&self) -> Option<Utf8Bom> {
1280        self.mode.utf8bom
1281    }
1282
1283    /// # Description
1284    ///
1285    /// -B 1 / -B 0
1286    ///
1287    /// Override the client-side `filesys.utf8bom` setting, controlling the
1288    /// presence of the byte-order-mark in utf8 type files. With `true`,
1289    /// write utf8 files with a BOM (`-B 1`); with `false`, do not write a
1290    /// BOM (`-B 0`).
1291    #[cfg(not(feature = "lt2023_1"))]
1292    pub fn set_write_utf8bom(&mut self, v: bool) -> &mut Self {
1293        self.mode.utf8bom = Some(if v { Utf8Bom::Yes } else { Utf8Bom::No });
1294        self
1295    }
1296
1297    /// # Description
1298    ///
1299    /// -B 1 / -B 0
1300    ///
1301    /// Override the client-side `filesys.utf8bom` setting, controlling the
1302    /// presence of the byte-order-mark in utf8 type files. With `true`,
1303    /// write utf8 files with a BOM (`-B 1`); with `false`, do not write a
1304    /// BOM (`-B 0`).
1305    #[cfg(not(feature = "lt2023_1"))]
1306    pub fn write_utf8bom(mut self, v: bool) -> Self {
1307        self.mode.utf8bom = Some(if v { Utf8Bom::Yes } else { Utf8Bom::No });
1308        self
1309    }
1310
1311    /// # Description
1312    ///
1313    /// -B 2
1314    ///
1315    /// Override the client-side `filesys.utf8bom` setting to write the BOM
1316    /// only on Windows.
1317    #[cfg(not(feature = "lt2023_1"))]
1318    pub fn set_write_utf8bom_windows_only(&mut self) -> &mut Self {
1319        self.mode.utf8bom = Some(Utf8Bom::WindowsOnly);
1320        self
1321    }
1322
1323    /// # Description
1324    ///
1325    /// -B 2
1326    ///
1327    /// Override the client-side `filesys.utf8bom` setting to write the BOM
1328    /// only on Windows.
1329    #[cfg(not(feature = "lt2023_1"))]
1330    pub fn write_utf8bom_windows_only(mut self) -> Self {
1331        self.mode.utf8bom = Some(Utf8Bom::WindowsOnly);
1332        self
1333    }
1334
1335    /// # Description
1336    ///
1337    /// -L line-ending
1338    ///
1339    /// Allow the line ending of textual files to be explicitly specified as
1340    /// 'unix', 'win', or 'mac'.
1341    ///
1342    /// Returns the line ending currently set, or `None` if no line ending is
1343    /// set.
1344    #[cfg(not(feature = "lt2023_1"))]
1345    pub fn get_line_ending(&self) -> Option<LineEnding> {
1346        self.mode.line_ending
1347    }
1348
1349    /// # Description
1350    ///
1351    /// -L unix
1352    ///
1353    /// Allow the line ending of textual files to be explicitly specified as
1354    /// 'unix', 'win', or 'mac'.
1355    #[cfg(not(feature = "lt2023_1"))]
1356    pub fn set_line_ending_unix(&mut self) -> &mut Self {
1357        self.mode.line_ending = Some(LineEnding::Unix);
1358        self
1359    }
1360
1361    /// # Description
1362    ///
1363    /// -L unix
1364    ///
1365    /// Allow the line ending of textual files to be explicitly specified as
1366    /// 'unix', 'win', or 'mac'.
1367    #[cfg(not(feature = "lt2023_1"))]
1368    pub fn line_ending_unix(mut self) -> Self {
1369        self.mode.line_ending = Some(LineEnding::Unix);
1370        self
1371    }
1372
1373    /// # Description
1374    ///
1375    /// -L win
1376    ///
1377    /// Allow the line ending of textual files to be explicitly specified as
1378    /// 'unix', 'win', or 'mac'.
1379    #[cfg(not(feature = "lt2023_1"))]
1380    pub fn set_line_ending_win(&mut self) -> &mut Self {
1381        self.mode.line_ending = Some(LineEnding::Win);
1382        self
1383    }
1384
1385    /// # Description
1386    ///
1387    /// -L win
1388    ///
1389    /// Allow the line ending of textual files to be explicitly specified as
1390    /// 'unix', 'win', or 'mac'.
1391    #[cfg(not(feature = "lt2023_1"))]
1392    pub fn line_ending_win(mut self) -> Self {
1393        self.mode.line_ending = Some(LineEnding::Win);
1394        self
1395    }
1396
1397    /// # Description
1398    ///
1399    /// -L mac
1400    ///
1401    /// Allow the line ending of textual files to be explicitly specified as
1402    /// 'unix', 'win', or 'mac'.
1403    #[cfg(not(feature = "lt2023_1"))]
1404    pub fn set_line_ending_mac(&mut self) -> &mut Self {
1405        self.mode.line_ending = Some(LineEnding::Mac);
1406        self
1407    }
1408
1409    /// # Description
1410    ///
1411    /// -L mac
1412    ///
1413    /// Allow the line ending of textual files to be explicitly specified as
1414    /// 'unix', 'win', or 'mac'.
1415    #[cfg(not(feature = "lt2023_1"))]
1416    pub fn line_ending_mac(mut self) -> Self {
1417        self.mode.line_ending = Some(LineEnding::Mac);
1418        self
1419    }
1420
1421    /// # Description
1422    ///
1423    /// --ignore-changeview
1424    ///
1425    /// Remove the changelist limit on depot paths. See ChangeView in
1426    /// `p4 client`.
1427    #[cfg(not(feature = "lt2026_1"))]
1428    pub fn get_ignore_changeview(&self) -> bool {
1429        self.mode.ignore_changeview
1430    }
1431
1432    /// # Description
1433    ///
1434    /// --ignore-changeview
1435    ///
1436    /// Remove the changelist limit on depot paths. See ChangeView in
1437    /// `p4 client`.
1438    #[cfg(not(feature = "lt2026_1"))]
1439    pub fn set_ignore_changeview(&mut self, v: bool) -> &mut Self {
1440        self.mode.ignore_changeview = v;
1441        self
1442    }
1443
1444    /// # Description
1445    ///
1446    /// --ignore-changeview
1447    ///
1448    /// Remove the changelist limit on depot paths. See ChangeView in
1449    /// `p4 client`.
1450    #[cfg(not(feature = "lt2026_1"))]
1451    pub fn ignore_changeview(mut self, v: bool) -> Self {
1452        self.mode.ignore_changeview = v;
1453        self
1454    }
1455}
1456
1457#[cfg(not(feature = "lt2024_2"))]
1458impl Print<AttributeTraitMode> {
1459    /// # Description
1460    ///
1461    /// -T attribute
1462    ///
1463    /// Print the value of the specified non-encoded attribute of the specified
1464    /// file.
1465    pub fn get_attribute(&self) -> &str {
1466        &self.mode.attribute
1467    }
1468
1469    /// # Description
1470    ///
1471    /// -T attribute
1472    ///
1473    /// Print the value of the specified non-encoded attribute of the specified
1474    /// file.
1475    pub fn set_attribute(&mut self, v: impl Into<String>) -> &mut Self {
1476        self.mode.attribute = v.into();
1477        self
1478    }
1479
1480    /// # Description
1481    ///
1482    /// -T attribute
1483    ///
1484    /// Print the value of the specified non-encoded attribute of the specified
1485    /// file.
1486    pub fn attribute(mut self, v: impl Into<String>) -> Self {
1487        self.mode.attribute = v.into();
1488        self
1489    }
1490
1491    /// # Description
1492    ///
1493    /// -a
1494    ///
1495    /// For each file, print all revisions within a specified revision range,
1496    /// rather than only the highest revision in the range.
1497    pub fn get_all_revisions(&self) -> bool {
1498        self.mode.all_revisions
1499    }
1500
1501    /// # Description
1502    ///
1503    /// -a
1504    ///
1505    /// For each file, print all revisions within a specified revision range,
1506    /// rather than only the highest revision in the range.
1507    pub fn set_all_revisions(&mut self, v: bool) -> &mut Self {
1508        self.mode.all_revisions = v;
1509        self
1510    }
1511
1512    /// # Description
1513    ///
1514    /// -a
1515    ///
1516    /// For each file, print all revisions within a specified revision range,
1517    /// rather than only the highest revision in the range.
1518    pub fn all_revisions(mut self, v: bool) -> Self {
1519        self.mode.all_revisions = v;
1520        self
1521    }
1522
1523    /// # Description
1524    ///
1525    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1526    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1527    ///
1528    /// Redirect output to the specified output file on the local disk,
1529    /// preserving the same file type, attributes, and/or permission bits as
1530    /// the original file in the depot.
1531    pub fn get_redirect_output(&self) -> Option<&PathBuf> {
1532        self.mode.redirect_output.as_ref()
1533    }
1534
1535    /// # Description
1536    ///
1537    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1538    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1539    ///
1540    /// Redirect output to the specified output file on the local disk,
1541    /// preserving the same file type, attributes, and/or permission bits as
1542    /// the original file in the depot.
1543    pub fn set_redirect_output(&mut self, v: impl Into<PathBuf>) -> &mut Self {
1544        self.mode.redirect_output = Some(v.into());
1545        self
1546    }
1547
1548    /// # Description
1549    ///
1550    #[cfg_attr(feature = "lt2023_1", doc = "-o outfile")]
1551    #[cfg_attr(not(feature = "lt2023_1"), doc = "-o localfile")]
1552    ///
1553    /// Redirect output to the specified output file on the local disk,
1554    /// preserving the same file type, attributes, and/or permission bits as
1555    /// the original file in the depot.
1556    pub fn redirect_output(mut self, v: impl Into<PathBuf>) -> Self {
1557        self.mode.redirect_output = Some(v.into());
1558        self
1559    }
1560
1561    /// # Description
1562    ///
1563    /// -q
1564    ///
1565    /// Suppress the one-line file header normally added by the Helix Core
1566    /// Server.
1567    pub fn get_quiet_mode(&self) -> bool {
1568        self.mode.quiet_mode
1569    }
1570
1571    /// # Description
1572    ///
1573    /// -q
1574    ///
1575    /// Suppress the one-line file header normally added by the Helix Core
1576    /// Server.
1577    pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
1578        self.mode.quiet_mode = v;
1579        self
1580    }
1581
1582    /// # Description
1583    ///
1584    /// -q
1585    ///
1586    /// Suppress the one-line file header normally added by the Helix Core
1587    /// Server.
1588    pub fn quiet_mode(mut self, v: bool) -> Self {
1589        self.mode.quiet_mode = v;
1590        self
1591    }
1592
1593    /// # Description
1594    ///
1595    /// --ignore-changeview
1596    ///
1597    /// Remove the changelist limit on depot paths. See ChangeView in
1598    /// `p4 client`.
1599    #[cfg(not(feature = "lt2026_1"))]
1600    pub fn get_ignore_changeview(&self) -> bool {
1601        self.mode.ignore_changeview
1602    }
1603
1604    /// # Description
1605    ///
1606    /// --ignore-changeview
1607    ///
1608    /// Remove the changelist limit on depot paths. See ChangeView in
1609    /// `p4 client`.
1610    #[cfg(not(feature = "lt2026_1"))]
1611    pub fn set_ignore_changeview(&mut self, v: bool) -> &mut Self {
1612        self.mode.ignore_changeview = v;
1613        self
1614    }
1615
1616    /// # Description
1617    ///
1618    /// --ignore-changeview
1619    ///
1620    /// Remove the changelist limit on depot paths. See ChangeView in
1621    /// `p4 client`.
1622    #[cfg(not(feature = "lt2026_1"))]
1623    pub fn ignore_changeview(mut self, v: bool) -> Self {
1624        self.mode.ignore_changeview = v;
1625        self
1626    }
1627}
1628
1629impl<M: ExclusiveOption> SubCommand for Print<M> {
1630    fn name(&self) -> &str {
1631        "print"
1632    }
1633
1634    fn inject_local_args(&self, command: &mut Command) {
1635        self.mode.inject_args(command);
1636    }
1637
1638    fn global_opts(&self) -> Option<&GlobalOpts> {
1639        Some(&self.global_opts)
1640    }
1641}
1642
1643#[cfg(test)]
1644mod tests {
1645    use super::*;
1646    use crate::cmd::args_of;
1647
1648    /// Dry-run checks of the assembled `p4 print` command line; no process is
1649    /// spawned.
1650    #[test]
1651    fn without_options() {
1652        let print = Print::new("p4", GlobalOpts::new());
1653
1654        assert_eq!(args_of(&print.setup_command("p4")), ["print"]);
1655    }
1656
1657    #[test]
1658    fn standard_mode_all_options() {
1659        let print = Print::new("p4", GlobalOpts::new())
1660            .all_revisions(true)
1661            .archive_depots(true)
1662            .suppress_keyword_expansion(true)
1663            .redirect_output("out.bin")
1664            .quiet_mode(true)
1665            .limit(5);
1666        #[cfg(not(feature = "lt2022_1"))]
1667        let print = print.offset(100).size(200);
1668        #[cfg(not(feature = "lt2023_1"))]
1669        let print = print.charset("utf8").write_utf8bom(true).line_ending_unix();
1670        #[cfg(not(feature = "lt2026_1"))]
1671        let print = print.ignore_changeview(true);
1672
1673        let mut expected = vec!["print", "-a", "-A"];
1674        #[cfg(feature = "lt2022_1")]
1675        expected.push("-k");
1676        #[cfg(not(feature = "lt2022_1"))]
1677        expected.push("-K");
1678        expected.extend(["-o", "out.bin", "-q", "-m", "5"]);
1679        #[cfg(not(feature = "lt2022_1"))]
1680        expected.extend(["--offset", "100", "--size", "200"]);
1681        #[cfg(not(feature = "lt2023_1"))]
1682        expected.extend(["-Q", "utf8", "-B", "1", "-L", "unix"]);
1683        #[cfg(not(feature = "lt2026_1"))]
1684        expected.push("--ignore-changeview");
1685
1686        assert_eq!(args_of(&print.setup_command("p4")), expected);
1687    }
1688
1689    #[test]
1690    fn standard_mode_set_style() {
1691        let mut print = Print::new("p4", GlobalOpts::new()).limit(5);
1692        print.set_all_revisions(true).set_quiet_mode(true);
1693        #[cfg(not(feature = "lt2022_1"))]
1694        print.set_offset(100).set_size(200);
1695
1696        let expected = {
1697            #[cfg_attr(feature = "lt2022_1", allow(unused_mut))]
1698            let mut v = vec!["print", "-a", "-q", "-m", "5"];
1699            #[cfg(not(feature = "lt2022_1"))]
1700            v.extend(["--offset", "100", "--size", "200"]);
1701            v
1702        };
1703
1704        assert_eq!(args_of(&print.setup_command("p4")), expected);
1705    }
1706
1707    #[test]
1708    fn unload_depot_mode() {
1709        let print = Print::new("p4", GlobalOpts::new()).from_unload_depot();
1710
1711        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-U"]);
1712    }
1713
1714    #[cfg(not(feature = "lt2023_1"))]
1715    #[test]
1716    fn line_ending_is_passed_as_separate_args() {
1717        let print = Print::new("p4", GlobalOpts::new()).line_ending_win();
1718
1719        assert_eq!(print.get_line_ending(), Some(LineEnding::Win));
1720        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-L", "win"]);
1721    }
1722
1723    #[cfg(not(feature = "lt2023_1"))]
1724    #[test]
1725    fn write_utf8bom_bool() {
1726        let yes = Print::new("p4", GlobalOpts::new()).write_utf8bom(true);
1727        assert_eq!(yes.get_utf8bom(), Some(Utf8Bom::Yes));
1728        assert_eq!(args_of(&yes.setup_command("p4")), ["print", "-B", "1"]);
1729
1730        let no = Print::new("p4", GlobalOpts::new()).write_utf8bom(false);
1731        assert_eq!(no.get_utf8bom(), Some(Utf8Bom::No));
1732        assert_eq!(args_of(&no.setup_command("p4")), ["print", "-B", "0"]);
1733    }
1734
1735    #[cfg(not(feature = "lt2023_1"))]
1736    #[test]
1737    fn write_utf8bom_windows_only() {
1738        let print = Print::new("p4", GlobalOpts::new()).write_utf8bom_windows_only();
1739
1740        assert_eq!(print.get_utf8bom(), Some(Utf8Bom::WindowsOnly));
1741        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-B", "2"]);
1742    }
1743
1744    #[cfg(not(feature = "lt2023_1"))]
1745    #[test]
1746    fn utf8bom_set_style_can_replace() {
1747        let mut print = Print::new("p4", GlobalOpts::new()).write_utf8bom(true);
1748        print.set_write_utf8bom(false);
1749        assert_eq!(print.get_utf8bom(), Some(Utf8Bom::No));
1750
1751        print.set_write_utf8bom_windows_only();
1752        assert_eq!(print.get_utf8bom(), Some(Utf8Bom::WindowsOnly));
1753        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-B", "2"]);
1754    }
1755
1756    #[cfg(not(feature = "lt2024_2"))]
1757    #[test]
1758    fn attribute_trait_mode() {
1759        let print = Print::new("p4", GlobalOpts::new()).extract_attribute("desc");
1760
1761        assert_eq!(print.get_attribute(), "desc");
1762        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-T", "desc"]);
1763    }
1764
1765    #[cfg(not(feature = "lt2024_2"))]
1766    #[test]
1767    fn attribute_trait_mode_with_allowed_options() {
1768        let print = Print::new("p4", GlobalOpts::new())
1769            .extract_attribute("desc")
1770            .all_revisions(true)
1771            .quiet_mode(true)
1772            .redirect_output("out.bin");
1773        #[cfg(not(feature = "lt2026_1"))]
1774        let print = print.ignore_changeview(true);
1775
1776        let mut expected = vec!["print", "-T", "desc", "-a", "-o", "out.bin", "-q"];
1777        #[cfg(not(feature = "lt2026_1"))]
1778        expected.push("--ignore-changeview");
1779
1780        assert_eq!(args_of(&print.setup_command("p4")), expected);
1781    }
1782
1783    #[cfg(not(feature = "lt2024_2"))]
1784    #[test]
1785    fn attribute_name_can_be_replaced() {
1786        let mut print = Print::new("p4", GlobalOpts::new()).extract_attribute("old");
1787        print.set_attribute("new");
1788
1789        assert_eq!(print.get_attribute(), "new");
1790        assert_eq!(args_of(&print.setup_command("p4")), ["print", "-T", "new"]);
1791    }
1792
1793    #[cfg(not(feature = "lt2026_1"))]
1794    #[test]
1795    fn ignore_changeview_standard_mode() {
1796        let print = Print::new("p4", GlobalOpts::new()).ignore_changeview(true);
1797
1798        assert!(print.get_ignore_changeview());
1799        assert_eq!(
1800            args_of(&print.setup_command("p4")),
1801            ["print", "--ignore-changeview"]
1802        );
1803    }
1804}