Skip to main content

perforce_cli/cmd/
print.rs

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