Skip to main content

perforce_cli/cmd/
describe.rs

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