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