Skip to main content

perforce_cli/cmd/
describe.rs

1use std::{
2    ffi::OsStr,
3    path::{Path, PathBuf},
4    process::{Child, Command, Stdio},
5};
6
7use super::{DiffOptions, ExclusiveOption, SubCommand, Unselected};
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12/// Short summary output of `p4 describe` (`-s`): display a shortened output
13/// that excludes the files' diffs.
14///
15/// Entered with [`Describe::short_summary_output`].
16#[derive(Debug, Clone, Copy, Default)]
17pub struct ShortSummaryMode;
18
19impl ExclusiveOption for ShortSummaryMode {
20    fn inject_args(&self, command: &mut Command) {
21        command.arg("-s");
22    }
23}
24
25#[cfg_attr(
26    feature = "lt2017_2",
27    doc = "Detail diff output of `p4 describe` (`-d`).",
28    doc = "",
29    doc = "Entered with [`Describe::diff_options`]."
30)]
31#[cfg_attr(
32    not(feature = "lt2017_2"),
33    doc = "Detail diff output of `p4 describe` (`-a` and `-d`).",
34    doc = "",
35    doc = "Entered with [`Describe::display_added_text_content`] or",
36    doc = "[`Describe::diff_options`]."
37)]
38#[derive(Debug, Clone, Default)]
39pub struct DetailDiffMode {
40    diff_options: Option<DiffOptions>,
41
42    #[cfg(not(feature = "lt2017_2"))]
43    display_added_text_content: bool,
44}
45
46impl ExclusiveOption for DetailDiffMode {
47    fn inject_args(&self, command: &mut Command) {
48        #[cfg(not(feature = "lt2017_2"))]
49        if self.display_added_text_content {
50            command.arg("-a");
51        }
52
53        if let Some(diff_options) = &self.diff_options {
54            diff_options.inject_arg(command);
55        }
56    }
57}
58
59#[cfg_attr(
60    feature = "lt2014_2",
61    doc = "`p4 [g-opts] describe [ -dflags -s -S -f -O ] changelist...`"
62)]
63#[cfg_attr(
64    all(feature = "lt2015_1", not(feature = "lt2014_2")),
65    doc = "`p4 [g-opts] describe [ -doptions -s -S -f -O ] changelist...`"
66)]
67#[cfg_attr(
68    all(feature = "lt2015_2", not(feature = "lt2015_1")),
69    doc = "`p4 [g-opts] describe [-doptions] [-s -S -f -O] changelist …`"
70)]
71#[cfg_attr(
72    all(feature = "lt2016_1", not(feature = "lt2015_2")),
73    doc = "`p4 [g-opts] describe [-doptions] [-s -S -f -O -I] changelist …`"
74)]
75#[cfg_attr(
76    all(feature = "lt2017_1", not(feature = "lt2016_1")),
77    doc = "`p4 [g-opts] describe [-doptions] [-s -S -f -O -I] changelist ...`"
78)]
79#[cfg_attr(
80    all(feature = "lt2017_2", not(feature = "lt2017_1")),
81    doc = "`p4 [g-opts] describe [-doptions] [-f -I -m -O -s -S] changelist ...`"
82)]
83#[cfg_attr(
84    not(feature = "lt2017_2"),
85    doc = "`p4 [g-opts] describe [-doptions] [-a -f -I -m -O -s -S] changelist ...`"
86)]
87///
88#[cfg_attr(
89    feature = "lt2017_2",
90    doc = "Provides information about changelists and the changelists' files."
91)]
92#[cfg_attr(
93    all(feature = "lt2019_1", not(feature = "lt2017_2")),
94    doc = "Provides information about changelists and files in the changelists."
95)]
96#[cfg_attr(
97    not(feature = "lt2019_1"),
98    doc = "Provides information about changelists, as well as files in the",
99    doc = "changelists, and the path of the open stream, if a stream is open."
100)]
101#[cfg_attr(
102    all(feature = "lt2018_1", not(feature = "lt2017_2")),
103    doc = "",
104    doc = "# Note",
105    doc = "",
106    doc = "If the depot is of type `graph`, displays a commit description. See",
107    doc = "the command-line help for `p4 help-graph describe`."
108)]
109///
110/// The `M` type parameter tracks the output mode at compile time. The
111/// default [`Unselected`] state offers neither `-s` nor
112#[cfg_attr(
113    feature = "lt2017_2",
114    doc = "`-d`; [`Self::short_summary_output`] transitions to the [`ShortSummaryMode`] state, while [`Self::diff_options`] transitions to the [`DetailDiffMode`] state."
115)]
116#[cfg_attr(
117    not(feature = "lt2017_2"),
118    doc = "`-a`/`-d`; [`Self::short_summary_output`] transitions to the [`ShortSummaryMode`] state, while [`Self::display_added_text_content`] and [`Self::diff_options`] transition to the [`DetailDiffMode`] state."
119)]
120#[derive(Debug, Clone, Default)]
121pub struct Describe<M = Unselected> {
122    bin: PathBuf,
123
124    global_opts: GlobalOpts,
125
126    force_restricted: bool,
127
128    #[cfg(not(feature = "lt2015_2"))]
129    use_identity: bool,
130
131    #[cfg(not(feature = "lt2017_1"))]
132    limit: Option<u64>,
133
134    use_original_number: bool,
135
136    include_shelved_files: bool,
137
138    mode: M,
139}
140
141impl Describe<Unselected> {
142    /// Creates a new `p4 describe` command.
143    ///
144    /// `bin` is the path to the Perforce command-line executable.
145    pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
146        Self {
147            bin: bin.into(),
148            global_opts,
149            force_restricted: false,
150            #[cfg(not(feature = "lt2015_2"))]
151            use_identity: false,
152            #[cfg(not(feature = "lt2017_1"))]
153            limit: None,
154            use_original_number: false,
155            include_shelved_files: false,
156            mode: Unselected,
157        }
158    }
159
160    /// # Description
161    ///
162    /// -s
163    ///
164    /// Display a shortened output that excludes
165    #[cfg_attr(feature = "lt2017_1", doc = "the files' diffs.")]
166    #[cfg_attr(not(feature = "lt2017_1"), doc = "the diffs of the files.")]
167    ///
168    /// Transitions this command to the [`ShortSummaryMode`] state.
169    pub fn short_summary_output(self) -> Describe<ShortSummaryMode> {
170        Describe {
171            bin: self.bin,
172            global_opts: self.global_opts,
173            force_restricted: self.force_restricted,
174            #[cfg(not(feature = "lt2015_2"))]
175            use_identity: self.use_identity,
176            #[cfg(not(feature = "lt2017_1"))]
177            limit: self.limit,
178            use_original_number: self.use_original_number,
179            include_shelved_files: self.include_shelved_files,
180            mode: ShortSummaryMode,
181        }
182    }
183
184    /// # Description
185    ///
186    /// -a
187    ///
188    /// For text files only (ignores binary files):
189    ///
190    /// - For shelved files, shows the content for "open for add" (pending) files.
191    /// - For submitted files, shows the content of added files.
192    ///
193    /// Transitions this command to the [`DetailDiffMode`] state with `-a`
194    /// set according to `v`.
195    #[cfg(not(feature = "lt2017_2"))]
196    pub fn display_added_text_content(self, v: bool) -> Describe<DetailDiffMode> {
197        Describe {
198            bin: self.bin,
199            global_opts: self.global_opts,
200            force_restricted: self.force_restricted,
201            #[cfg(not(feature = "lt2015_2"))]
202            use_identity: self.use_identity,
203            #[cfg(not(feature = "lt2017_1"))]
204            limit: self.limit,
205            use_original_number: self.use_original_number,
206            include_shelved_files: self.include_shelved_files,
207            mode: DetailDiffMode {
208                diff_options: None,
209                display_added_text_content: v,
210            },
211        }
212    }
213
214    /// # Description
215    ///
216    #[cfg_attr(feature = "lt2014_2", doc = "-dflags")]
217    #[cfg_attr(not(feature = "lt2014_2"), doc = "-doptions")]
218    ///
219    /// Runs the diff routine with one of a subset of the standard UNIX diff
220    #[cfg_attr(
221        feature = "lt2014_2",
222        doc = "flags. See the Usage Notes below for a flag listing."
223    )]
224    #[cfg_attr(
225        all(feature = "lt2015_1", not(feature = "lt2014_2")),
226        doc = "options. See the Usage Notes below for a option listing."
227    )]
228    #[cfg_attr(
229        all(feature = "lt2024_1", not(feature = "lt2015_1")),
230        doc = "options. See Usage Notes for an option listing."
231    )]
232    #[cfg_attr(
233        not(feature = "lt2024_1"),
234        doc = "options. See Usage notes for an option listing."
235    )]
236    ///
237    /// Transitions this command to the [`DetailDiffMode`] state with the
238    /// diff `options` set.
239    pub fn diff_options(self, v: impl Into<DiffOptions>) -> Describe<DetailDiffMode> {
240        Describe {
241            bin: self.bin,
242            global_opts: self.global_opts,
243            force_restricted: self.force_restricted,
244            #[cfg(not(feature = "lt2015_2"))]
245            use_identity: self.use_identity,
246            #[cfg(not(feature = "lt2017_1"))]
247            limit: self.limit,
248            use_original_number: self.use_original_number,
249            include_shelved_files: self.include_shelved_files,
250            mode: DetailDiffMode {
251                diff_options: Some(v.into()),
252                #[cfg(not(feature = "lt2017_2"))]
253                display_added_text_content: false,
254            },
255        }
256    }
257}
258
259impl<M: ExclusiveOption, S, I> ParameterizedSpawn<(S,)> for Describe<M>
260where
261    S: IntoIterator<Item = I>,
262    I: AsRef<OsStr>,
263{
264    type Output = Child;
265    type Error = std::io::Error;
266
267    /// Spawns `p4 describe` for the given changelists as a child process with
268    /// piped standard output and error streams; use the returned [`Child`]
269    /// handle to wait for it or interact with it.
270    fn spawn_with(&mut self, (changelists,): (S,)) -> Result<Self::Output, Self::Error> {
271        self.setup_command(&self.bin)
272            .args(changelists)
273            .stdout(Stdio::piped())
274            .stderr(Stdio::piped())
275            .spawn()
276    }
277}
278
279impl<M: ExclusiveOption> Describe<M> {
280    /// Get the Perforce command path.
281    pub fn get_bin(&self) -> &Path {
282        &self.bin
283    }
284
285    /// Set the Perforce command path.
286    pub fn set_bin(&mut self, bin: impl Into<PathBuf>) -> &mut Self {
287        self.bin = bin.into();
288        self
289    }
290
291    /// Set the Perforce command path.
292    pub fn bin(mut self, bin: impl Into<PathBuf>) -> Self {
293        self.set_bin(bin);
294        self
295    }
296
297    /// # Description
298    ///
299    /// g-opts
300    ///
301    #[cfg_attr(
302        feature = "lt2014_2",
303        doc = "See the [Global Options](GlobalOpts) section."
304    )]
305    #[cfg_attr(
306        all(feature = "lt2015_1", not(feature = "lt2014_2")),
307        doc = "See the [“Global Options”](GlobalOpts) section."
308    )]
309    #[cfg_attr(
310        all(feature = "lt2017_1", not(feature = "lt2015_1")),
311        doc = "See [“Global Options”](GlobalOpts)."
312    )]
313    #[cfg_attr(
314        all(feature = "lt2018_2", not(feature = "lt2017_1")),
315        doc = "See [Global Options](GlobalOpts)."
316    )]
317    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
318    pub fn get_global_opts(&self) -> &GlobalOpts {
319        &self.global_opts
320    }
321
322    /// # Description
323    ///
324    /// g-opts
325    ///
326    #[cfg_attr(
327        feature = "lt2014_2",
328        doc = "See the [Global Options](GlobalOpts) section."
329    )]
330    #[cfg_attr(
331        all(feature = "lt2015_1", not(feature = "lt2014_2")),
332        doc = "See the [“Global Options”](GlobalOpts) section."
333    )]
334    #[cfg_attr(
335        all(feature = "lt2017_1", not(feature = "lt2015_1")),
336        doc = "See [“Global Options”](GlobalOpts)."
337    )]
338    #[cfg_attr(
339        all(feature = "lt2018_2", not(feature = "lt2017_1")),
340        doc = "See [Global Options](GlobalOpts)."
341    )]
342    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
343    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
344        self.global_opts = v;
345        self
346    }
347
348    /// # Description
349    ///
350    /// g-opts
351    ///
352    #[cfg_attr(
353        feature = "lt2014_2",
354        doc = "See the [Global Options](GlobalOpts) section."
355    )]
356    #[cfg_attr(
357        all(feature = "lt2015_1", not(feature = "lt2014_2")),
358        doc = "See the [“Global Options”](GlobalOpts) section."
359    )]
360    #[cfg_attr(
361        all(feature = "lt2017_1", not(feature = "lt2015_1")),
362        doc = "See [“Global Options”](GlobalOpts)."
363    )]
364    #[cfg_attr(
365        all(feature = "lt2018_2", not(feature = "lt2017_1")),
366        doc = "See [Global Options](GlobalOpts)."
367    )]
368    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
369    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
370        self.global_opts = v;
371        self
372    }
373
374    /// # Description
375    ///
376    /// -f
377    ///
378    /// Force the display of descriptions for restricted changelists. This
379    #[cfg_attr(feature = "lt2014_2", doc = "flag requires `admin` permission.")]
380    #[cfg_attr(not(feature = "lt2014_2"), doc = "option requires `admin` permission.")]
381    pub fn get_force_restricted(&self) -> bool {
382        self.force_restricted
383    }
384
385    /// # Description
386    ///
387    /// -f
388    ///
389    /// Force the display of descriptions for restricted changelists. This
390    #[cfg_attr(feature = "lt2014_2", doc = "flag requires `admin` permission.")]
391    #[cfg_attr(not(feature = "lt2014_2"), doc = "option requires `admin` permission.")]
392    pub fn set_force_restricted(&mut self, v: bool) -> &mut Self {
393        self.force_restricted = v;
394        self
395    }
396
397    /// # Description
398    ///
399    /// -f
400    ///
401    /// Force the display of descriptions for restricted changelists. This
402    #[cfg_attr(feature = "lt2014_2", doc = "flag requires `admin` permission.")]
403    #[cfg_attr(not(feature = "lt2014_2"), doc = "option requires `admin` permission.")]
404    pub fn force_restricted(mut self, v: bool) -> Self {
405        self.force_restricted = v;
406        self
407    }
408
409    /// # Description
410    ///
411    /// -I
412    ///
413    /// Specifies that the changelist number is the
414    #[cfg_attr(feature = "lt2017_1", doc = "Identity field of a changelist.")]
415    #[cfg_attr(not(feature = "lt2017_1"), doc = "`Identity` field of a changelist.")]
416    #[cfg(not(feature = "lt2015_2"))]
417    pub fn get_use_identity(&self) -> bool {
418        self.use_identity
419    }
420
421    /// # Description
422    ///
423    /// -I
424    ///
425    /// Specifies that the changelist number is the
426    #[cfg_attr(feature = "lt2017_1", doc = "Identity field of a changelist.")]
427    #[cfg_attr(not(feature = "lt2017_1"), doc = "`Identity` field of a changelist.")]
428    #[cfg(not(feature = "lt2015_2"))]
429    pub fn set_use_identity(&mut self, v: bool) -> &mut Self {
430        self.use_identity = v;
431        self
432    }
433
434    /// # Description
435    ///
436    /// -I
437    ///
438    /// Specifies that the changelist number is the
439    #[cfg_attr(feature = "lt2017_1", doc = "Identity field of a changelist.")]
440    #[cfg_attr(not(feature = "lt2017_1"), doc = "`Identity` field of a changelist.")]
441    #[cfg(not(feature = "lt2015_2"))]
442    pub fn use_identity(mut self, v: bool) -> Self {
443        self.use_identity = v;
444        self
445    }
446
447    /// # Description
448    ///
449    /// -m max
450    ///
451    /// Limits files to the first *max* number of files. The following
452    /// example alphabetically lists (and diffs) two files affected by
453    /// changelist 765 and two files affected by changelist 987:
454    /// `p4 describe -m 2 765 987`
455    #[cfg(not(feature = "lt2017_1"))]
456    pub fn get_limit(&self) -> Option<u64> {
457        self.limit
458    }
459
460    /// # Description
461    ///
462    /// -m max
463    ///
464    /// Limits files to the first *max* number of files. The following
465    /// example alphabetically lists (and diffs) two files affected by
466    /// changelist 765 and two files affected by changelist 987:
467    /// `p4 describe -m 2 765 987`
468    #[cfg(not(feature = "lt2017_1"))]
469    pub fn set_limit(&mut self, v: u64) -> &mut Self {
470        self.limit = Some(v);
471        self
472    }
473
474    /// # Description
475    ///
476    /// -m max
477    ///
478    /// Limits files to the first *max* number of files. The following
479    /// example alphabetically lists (and diffs) two files affected by
480    /// changelist 765 and two files affected by changelist 987:
481    /// `p4 describe -m 2 765 987`
482    #[cfg(not(feature = "lt2017_1"))]
483    pub fn limit(mut self, v: u64) -> Self {
484        self.limit = Some(v);
485        self
486    }
487
488    /// # Description
489    ///
490    /// -O
491    ///
492    /// If a changelist was renumbered on submit, and you know only the
493    /// original changelist number, use `-O` and the original changelist
494    /// number to describe the changelist.
495    pub fn get_use_original_number(&self) -> bool {
496        self.use_original_number
497    }
498
499    /// # Description
500    ///
501    /// -O
502    ///
503    /// If a changelist was renumbered on submit, and you know only the
504    /// original changelist number, use `-O` and the original changelist
505    /// number to describe the changelist.
506    pub fn set_use_original_number(&mut self, v: bool) -> &mut Self {
507        self.use_original_number = v;
508        self
509    }
510
511    /// # Description
512    ///
513    /// -O
514    ///
515    /// If a changelist was renumbered on submit, and you know only the
516    /// original changelist number, use `-O` and the original changelist
517    /// number to describe the changelist.
518    pub fn use_original_number(mut self, v: bool) -> Self {
519        self.use_original_number = v;
520        self
521    }
522
523    /// # Description
524    ///
525    /// -S
526    ///
527    #[cfg_attr(
528        feature = "lt2017_1",
529        doc = "Display files shelved for the specified changelist, including",
530        doc = "diffs of those files against their previous depot revision."
531    )]
532    #[cfg_attr(
533        all(feature = "lt2020_2", not(feature = "lt2017_1")),
534        doc = "Display the names of files shelved for the specified changelist,",
535        doc = "including the diff of each file against its previous depot",
536        doc = "revision."
537    )]
538    #[cfg_attr(
539        not(feature = "lt2020_2"),
540        doc = "Lists files that are shelved for the pending changelist and",
541        doc = "displays diffs of the files against their previous revision."
542    )]
543    pub fn get_include_shelved_files(&self) -> bool {
544        self.include_shelved_files
545    }
546
547    /// # Description
548    ///
549    /// -S
550    ///
551    #[cfg_attr(
552        feature = "lt2017_1",
553        doc = "Display files shelved for the specified changelist, including",
554        doc = "diffs of those files against their previous depot revision."
555    )]
556    #[cfg_attr(
557        all(feature = "lt2020_2", not(feature = "lt2017_1")),
558        doc = "Display the names of files shelved for the specified changelist,",
559        doc = "including the diff of each file against its previous depot",
560        doc = "revision."
561    )]
562    #[cfg_attr(
563        not(feature = "lt2020_2"),
564        doc = "Lists files that are shelved for the pending changelist and",
565        doc = "displays diffs of the files against their previous revision."
566    )]
567    pub fn set_include_shelved_files(&mut self, v: bool) -> &mut Self {
568        self.include_shelved_files = v;
569        self
570    }
571
572    /// # Description
573    ///
574    /// -S
575    ///
576    #[cfg_attr(
577        feature = "lt2017_1",
578        doc = "Display files shelved for the specified changelist, including",
579        doc = "diffs of those files against their previous depot revision."
580    )]
581    #[cfg_attr(
582        all(feature = "lt2020_2", not(feature = "lt2017_1")),
583        doc = "Display the names of files shelved for the specified changelist,",
584        doc = "including the diff of each file against its previous depot",
585        doc = "revision."
586    )]
587    #[cfg_attr(
588        not(feature = "lt2020_2"),
589        doc = "Lists files that are shelved for the pending changelist and",
590        doc = "displays diffs of the files against their previous revision."
591    )]
592    pub fn include_shelved_files(mut self, v: bool) -> Self {
593        self.include_shelved_files = v;
594        self
595    }
596}
597
598impl Describe<DetailDiffMode> {
599    /// # Description
600    ///
601    #[cfg_attr(feature = "lt2014_2", doc = "-dflags")]
602    #[cfg_attr(not(feature = "lt2014_2"), doc = "-doptions")]
603    ///
604    /// Runs the diff routine with one of a subset of the standard UNIX diff
605    #[cfg_attr(
606        feature = "lt2014_2",
607        doc = "flags. See the Usage Notes below for a flag listing."
608    )]
609    #[cfg_attr(
610        all(feature = "lt2015_1", not(feature = "lt2014_2")),
611        doc = "options. See the Usage Notes below for a option listing."
612    )]
613    #[cfg_attr(
614        all(feature = "lt2024_1", not(feature = "lt2015_1")),
615        doc = "options. See Usage Notes for an option listing."
616    )]
617    #[cfg_attr(
618        not(feature = "lt2024_1"),
619        doc = "options. See Usage notes for an option listing."
620    )]
621    pub fn get_diff_options(&self) -> Option<&DiffOptions> {
622        self.mode.diff_options.as_ref()
623    }
624
625    /// # Description
626    ///
627    #[cfg_attr(feature = "lt2014_2", doc = "-dflags")]
628    #[cfg_attr(not(feature = "lt2014_2"), doc = "-doptions")]
629    ///
630    /// Runs the diff routine with one of a subset of the standard UNIX diff
631    #[cfg_attr(
632        feature = "lt2014_2",
633        doc = "flags. See the Usage Notes below for a flag listing."
634    )]
635    #[cfg_attr(
636        all(feature = "lt2015_1", not(feature = "lt2014_2")),
637        doc = "options. See the Usage Notes below for a option listing."
638    )]
639    #[cfg_attr(
640        all(feature = "lt2024_1", not(feature = "lt2015_1")),
641        doc = "options. See Usage Notes for an option listing."
642    )]
643    #[cfg_attr(
644        not(feature = "lt2024_1"),
645        doc = "options. See Usage notes for an option listing."
646    )]
647    pub fn set_diff_options(&mut self, v: impl Into<DiffOptions>) -> &mut Self {
648        self.mode.diff_options = Some(v.into());
649        self
650    }
651
652    /// # Description
653    ///
654    /// -a
655    ///
656    /// For text files only (ignores binary files):
657    ///
658    /// - For shelved files, shows the content for "open for add" (pending) files.
659    /// - For submitted files, shows the content of added files.
660    #[cfg(not(feature = "lt2017_2"))]
661    pub fn get_display_added_text_content(&self) -> bool {
662        self.mode.display_added_text_content
663    }
664
665    /// # Description
666    ///
667    /// -a
668    ///
669    /// For text files only (ignores binary files):
670    ///
671    /// - For shelved files, shows the content for "open for add" (pending) files.
672    /// - For submitted files, shows the content of added files.
673    #[cfg(not(feature = "lt2017_2"))]
674    pub fn set_display_added_text_content(&mut self, v: bool) -> &mut Self {
675        self.mode.display_added_text_content = v;
676        self
677    }
678}
679
680impl<M: ExclusiveOption> SubCommand for Describe<M> {
681    fn name(&self) -> &str {
682        "describe"
683    }
684
685    fn inject_local_args(&self, command: &mut Command) {
686        self.mode.inject_args(command);
687
688        if self.force_restricted {
689            command.arg("-f");
690        }
691
692        #[cfg(not(feature = "lt2015_2"))]
693        if self.use_identity {
694            command.arg("-I");
695        }
696
697        #[cfg(not(feature = "lt2017_1"))]
698        if let Some(max) = self.limit {
699            command.arg("-m").arg(max.to_string());
700        }
701
702        if self.use_original_number {
703            command.arg("-O");
704        }
705
706        if self.include_shelved_files {
707            command.arg("-S");
708        }
709    }
710
711    fn global_opts(&self) -> Option<&GlobalOpts> {
712        Some(&self.global_opts)
713    }
714}
715
716#[cfg(test)]
717mod tests {
718    use super::*;
719    use crate::cmd::DiffOptionsBuilder;
720    use crate::cmd::args_of;
721
722    #[test]
723    fn bin_accessors_update_executable_path() {
724        let mut command = Describe::new("p4", GlobalOpts::new());
725        assert_eq!(command.get_bin(), Path::new("p4"));
726
727        command.set_bin("/usr/local/bin/p4");
728        assert_eq!(command.get_bin(), Path::new("/usr/local/bin/p4"));
729
730        let command = command.bin("p4.exe");
731        assert_eq!(command.get_bin(), Path::new("p4.exe"));
732    }
733
734    /// Dry-run checks of the assembled `p4 describe` command line; no process
735    /// is spawned.
736    #[test]
737    fn without_options() {
738        let describe = Describe::new("p4", GlobalOpts::new());
739
740        assert_eq!(args_of(&describe.setup_command("p4")), ["describe"]);
741    }
742
743    #[test]
744    fn unselected_with_generic_options() {
745        let mut describe = Describe::new("p4", GlobalOpts::new());
746        describe.set_force_restricted(true);
747        #[cfg(not(feature = "lt2015_2"))]
748        describe.set_use_identity(true);
749        #[cfg(not(feature = "lt2017_1"))]
750        describe.set_limit(2);
751        describe
752            .set_use_original_number(true)
753            .set_include_shelved_files(true);
754
755        let mut expected = vec!["describe"];
756        expected.push("-f");
757        #[cfg(not(feature = "lt2015_2"))]
758        expected.push("-I");
759        #[cfg(not(feature = "lt2017_1"))]
760        expected.extend(["-m", "2"]);
761        expected.extend(["-O", "-S"]);
762
763        assert_eq!(args_of(&describe.setup_command("p4")), expected);
764    }
765
766    #[test]
767    fn short_summary_mode() {
768        let describe = Describe::new("p4", GlobalOpts::new())
769            .force_restricted(true)
770            .short_summary_output();
771
772        assert_eq!(
773            args_of(&describe.setup_command("p4")),
774            ["describe", "-s", "-f"]
775        );
776    }
777
778    #[test]
779    fn mode_transition_preserves_generic_options() {
780        let mut describe = Describe::new("p4", GlobalOpts::new());
781        describe.set_force_restricted(true);
782        #[cfg(not(feature = "lt2017_1"))]
783        describe.set_limit(3);
784
785        let describe = describe.short_summary_output().include_shelved_files(true);
786
787        let mut expected = vec!["describe", "-s", "-f"];
788        #[cfg(not(feature = "lt2017_1"))]
789        expected.extend(["-m", "3"]);
790        expected.push("-S");
791
792        assert_eq!(args_of(&describe.setup_command("p4")), expected);
793    }
794
795    #[test]
796    fn detail_diff_mode_via_diff_options() {
797        let describe =
798            Describe::new("p4", GlobalOpts::new()).diff_options(DiffOptionsBuilder::unified(None));
799
800        assert!(describe.get_diff_options().is_some());
801        assert_eq!(args_of(&describe.setup_command("p4")), ["describe", "-du"]);
802    }
803
804    #[test]
805    fn detail_diff_mode_set_diff_options() {
806        let mut describe =
807            Describe::new("p4", GlobalOpts::new()).diff_options(DiffOptionsBuilder::unified(None));
808        describe.set_diff_options(DiffOptionsBuilder::summary());
809
810        assert_eq!(
811            describe.get_diff_options(),
812            Some(&DiffOptions::from(DiffOptionsBuilder::summary()))
813        );
814        assert_eq!(args_of(&describe.setup_command("p4")), ["describe", "-ds"]);
815    }
816
817    #[cfg(not(feature = "lt2017_2"))]
818    #[test]
819    fn detail_diff_mode_via_display_added_text_content() {
820        let describe = Describe::new("p4", GlobalOpts::new()).display_added_text_content(true);
821
822        assert!(describe.get_display_added_text_content());
823        assert_eq!(describe.get_diff_options(), None);
824        assert_eq!(args_of(&describe.setup_command("p4")), ["describe", "-a"]);
825    }
826
827    #[cfg(not(feature = "lt2017_2"))]
828    #[test]
829    fn detail_diff_mode_combines_a_and_d() {
830        let mut describe = Describe::new("p4", GlobalOpts::new()).display_added_text_content(true);
831        describe.set_diff_options(DiffOptionsBuilder::unified(None));
832
833        assert_eq!(
834            args_of(&describe.setup_command("p4")),
835            ["describe", "-a", "-du"]
836        );
837    }
838
839    #[cfg(not(feature = "lt2017_1"))]
840    #[test]
841    fn limit_is_injected_as_separate_args() {
842        let mut describe = Describe::new("p4", GlobalOpts::new());
843        describe.set_limit(5);
844
845        assert_eq!(
846            args_of(&describe.setup_command("p4")),
847            ["describe", "-m", "5"]
848        );
849    }
850}