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#[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    /// # Description
281    ///
282    /// g-opts
283    ///
284    #[cfg_attr(
285        feature = "lt2014_2",
286        doc = "See the [Global Options](GlobalOpts) section."
287    )]
288    #[cfg_attr(
289        all(feature = "lt2015_1", not(feature = "lt2014_2")),
290        doc = "See the [“Global Options”](GlobalOpts) section."
291    )]
292    #[cfg_attr(
293        all(feature = "lt2017_1", not(feature = "lt2015_1")),
294        doc = "See [“Global Options”](GlobalOpts)."
295    )]
296    #[cfg_attr(
297        all(feature = "lt2018_2", not(feature = "lt2017_1")),
298        doc = "See [Global Options](GlobalOpts)."
299    )]
300    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
301    pub fn get_global_opts(&self) -> &GlobalOpts {
302        &self.global_opts
303    }
304
305    /// # Description
306    ///
307    /// g-opts
308    ///
309    #[cfg_attr(
310        feature = "lt2014_2",
311        doc = "See the [Global Options](GlobalOpts) section."
312    )]
313    #[cfg_attr(
314        all(feature = "lt2015_1", not(feature = "lt2014_2")),
315        doc = "See the [“Global Options”](GlobalOpts) section."
316    )]
317    #[cfg_attr(
318        all(feature = "lt2017_1", not(feature = "lt2015_1")),
319        doc = "See [“Global Options”](GlobalOpts)."
320    )]
321    #[cfg_attr(
322        all(feature = "lt2018_2", not(feature = "lt2017_1")),
323        doc = "See [Global Options](GlobalOpts)."
324    )]
325    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
326    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
327        self.global_opts = v;
328        self
329    }
330
331    /// # Description
332    ///
333    /// g-opts
334    ///
335    #[cfg_attr(
336        feature = "lt2014_2",
337        doc = "See the [Global Options](GlobalOpts) section."
338    )]
339    #[cfg_attr(
340        all(feature = "lt2015_1", not(feature = "lt2014_2")),
341        doc = "See the [“Global Options”](GlobalOpts) section."
342    )]
343    #[cfg_attr(
344        all(feature = "lt2017_1", not(feature = "lt2015_1")),
345        doc = "See [“Global Options”](GlobalOpts)."
346    )]
347    #[cfg_attr(
348        all(feature = "lt2018_2", not(feature = "lt2017_1")),
349        doc = "See [Global Options](GlobalOpts)."
350    )]
351    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
352    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
353        self.global_opts = v;
354        self
355    }
356
357    /// # Description
358    ///
359    /// -f
360    ///
361    /// Force the display of descriptions for restricted changelists. This
362    #[cfg_attr(feature = "lt2014_2", doc = "flag requires `admin` permission.")]
363    #[cfg_attr(not(feature = "lt2014_2"), doc = "option requires `admin` permission.")]
364    pub fn get_force_restricted(&self) -> bool {
365        self.force_restricted
366    }
367
368    /// # Description
369    ///
370    /// -f
371    ///
372    /// Force the display of descriptions for restricted changelists. This
373    #[cfg_attr(feature = "lt2014_2", doc = "flag requires `admin` permission.")]
374    #[cfg_attr(not(feature = "lt2014_2"), doc = "option requires `admin` permission.")]
375    pub fn set_force_restricted(&mut self, v: bool) -> &mut Self {
376        self.force_restricted = v;
377        self
378    }
379
380    /// # Description
381    ///
382    /// -f
383    ///
384    /// Force the display of descriptions for restricted changelists. This
385    #[cfg_attr(feature = "lt2014_2", doc = "flag requires `admin` permission.")]
386    #[cfg_attr(not(feature = "lt2014_2"), doc = "option requires `admin` permission.")]
387    pub fn force_restricted(mut self, v: bool) -> Self {
388        self.force_restricted = v;
389        self
390    }
391
392    /// # Description
393    ///
394    /// -I
395    ///
396    /// Specifies that the changelist number is the
397    #[cfg_attr(feature = "lt2017_1", doc = "Identity field of a changelist.")]
398    #[cfg_attr(not(feature = "lt2017_1"), doc = "`Identity` field of a changelist.")]
399    #[cfg(not(feature = "lt2015_2"))]
400    pub fn get_use_identity(&self) -> bool {
401        self.use_identity
402    }
403
404    /// # Description
405    ///
406    /// -I
407    ///
408    /// Specifies that the changelist number is the
409    #[cfg_attr(feature = "lt2017_1", doc = "Identity field of a changelist.")]
410    #[cfg_attr(not(feature = "lt2017_1"), doc = "`Identity` field of a changelist.")]
411    #[cfg(not(feature = "lt2015_2"))]
412    pub fn set_use_identity(&mut self, v: bool) -> &mut Self {
413        self.use_identity = v;
414        self
415    }
416
417    /// # Description
418    ///
419    /// -I
420    ///
421    /// Specifies that the changelist number is the
422    #[cfg_attr(feature = "lt2017_1", doc = "Identity field of a changelist.")]
423    #[cfg_attr(not(feature = "lt2017_1"), doc = "`Identity` field of a changelist.")]
424    #[cfg(not(feature = "lt2015_2"))]
425    pub fn use_identity(mut self, v: bool) -> Self {
426        self.use_identity = v;
427        self
428    }
429
430    /// # Description
431    ///
432    /// -m max
433    ///
434    /// Limits files to the first *max* number of files. The following
435    /// example alphabetically lists (and diffs) two files affected by
436    /// changelist 765 and two files affected by changelist 987:
437    /// `p4 describe -m 2 765 987`
438    #[cfg(not(feature = "lt2017_1"))]
439    pub fn get_limit(&self) -> Option<u64> {
440        self.limit
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 set_limit(&mut self, v: u64) -> &mut Self {
453        self.limit = Some(v);
454        self
455    }
456
457    /// # Description
458    ///
459    /// -m max
460    ///
461    /// Limits files to the first *max* number of files. The following
462    /// example alphabetically lists (and diffs) two files affected by
463    /// changelist 765 and two files affected by changelist 987:
464    /// `p4 describe -m 2 765 987`
465    #[cfg(not(feature = "lt2017_1"))]
466    pub fn limit(mut self, v: u64) -> Self {
467        self.limit = Some(v);
468        self
469    }
470
471    /// # Description
472    ///
473    /// -O
474    ///
475    /// If a changelist was renumbered on submit, and you know only the
476    /// original changelist number, use `-O` and the original changelist
477    /// number to describe the changelist.
478    pub fn get_use_original_number(&self) -> bool {
479        self.use_original_number
480    }
481
482    /// # Description
483    ///
484    /// -O
485    ///
486    /// If a changelist was renumbered on submit, and you know only the
487    /// original changelist number, use `-O` and the original changelist
488    /// number to describe the changelist.
489    pub fn set_use_original_number(&mut self, v: bool) -> &mut Self {
490        self.use_original_number = v;
491        self
492    }
493
494    /// # Description
495    ///
496    /// -O
497    ///
498    /// If a changelist was renumbered on submit, and you know only the
499    /// original changelist number, use `-O` and the original changelist
500    /// number to describe the changelist.
501    pub fn use_original_number(mut self, v: bool) -> Self {
502        self.use_original_number = v;
503        self
504    }
505
506    /// # Description
507    ///
508    /// -S
509    ///
510    #[cfg_attr(
511        feature = "lt2017_1",
512        doc = "Display files shelved for the specified changelist, including",
513        doc = "diffs of those files against their previous depot revision."
514    )]
515    #[cfg_attr(
516        all(feature = "lt2020_2", not(feature = "lt2017_1")),
517        doc = "Display the names of files shelved for the specified changelist,",
518        doc = "including the diff of each file against its previous depot",
519        doc = "revision."
520    )]
521    #[cfg_attr(
522        not(feature = "lt2020_2"),
523        doc = "Lists files that are shelved for the pending changelist and",
524        doc = "displays diffs of the files against their previous revision."
525    )]
526    pub fn get_include_shelved_files(&self) -> bool {
527        self.include_shelved_files
528    }
529
530    /// # Description
531    ///
532    /// -S
533    ///
534    #[cfg_attr(
535        feature = "lt2017_1",
536        doc = "Display files shelved for the specified changelist, including",
537        doc = "diffs of those files against their previous depot revision."
538    )]
539    #[cfg_attr(
540        all(feature = "lt2020_2", not(feature = "lt2017_1")),
541        doc = "Display the names of files shelved for the specified changelist,",
542        doc = "including the diff of each file against its previous depot",
543        doc = "revision."
544    )]
545    #[cfg_attr(
546        not(feature = "lt2020_2"),
547        doc = "Lists files that are shelved for the pending changelist and",
548        doc = "displays diffs of the files against their previous revision."
549    )]
550    pub fn set_include_shelved_files(&mut self, v: bool) -> &mut Self {
551        self.include_shelved_files = v;
552        self
553    }
554
555    /// # Description
556    ///
557    /// -S
558    ///
559    #[cfg_attr(
560        feature = "lt2017_1",
561        doc = "Display files shelved for the specified changelist, including",
562        doc = "diffs of those files against their previous depot revision."
563    )]
564    #[cfg_attr(
565        all(feature = "lt2020_2", not(feature = "lt2017_1")),
566        doc = "Display the names of files shelved for the specified changelist,",
567        doc = "including the diff of each file against its previous depot",
568        doc = "revision."
569    )]
570    #[cfg_attr(
571        not(feature = "lt2020_2"),
572        doc = "Lists files that are shelved for the pending changelist and",
573        doc = "displays diffs of the files against their previous revision."
574    )]
575    pub fn include_shelved_files(mut self, v: bool) -> Self {
576        self.include_shelved_files = v;
577        self
578    }
579}
580
581impl Describe<DetailDiffMode> {
582    /// # Description
583    ///
584    #[cfg_attr(feature = "lt2014_2", doc = "-dflags")]
585    #[cfg_attr(not(feature = "lt2014_2"), doc = "-doptions")]
586    ///
587    /// Runs the diff routine with one of a subset of the standard UNIX diff
588    #[cfg_attr(
589        feature = "lt2014_2",
590        doc = "flags. See the Usage Notes below for a flag listing."
591    )]
592    #[cfg_attr(
593        all(feature = "lt2015_1", not(feature = "lt2014_2")),
594        doc = "options. See the Usage Notes below for a option listing."
595    )]
596    #[cfg_attr(
597        all(feature = "lt2024_1", not(feature = "lt2015_1")),
598        doc = "options. See Usage Notes for an option listing."
599    )]
600    #[cfg_attr(
601        not(feature = "lt2024_1"),
602        doc = "options. See Usage notes for an option listing."
603    )]
604    pub fn get_diff_options(&self) -> Option<&DiffOptions> {
605        self.mode.diff_options.as_ref()
606    }
607
608    /// # Description
609    ///
610    #[cfg_attr(feature = "lt2014_2", doc = "-dflags")]
611    #[cfg_attr(not(feature = "lt2014_2"), doc = "-doptions")]
612    ///
613    /// Runs the diff routine with one of a subset of the standard UNIX diff
614    #[cfg_attr(
615        feature = "lt2014_2",
616        doc = "flags. See the Usage Notes below for a flag listing."
617    )]
618    #[cfg_attr(
619        all(feature = "lt2015_1", not(feature = "lt2014_2")),
620        doc = "options. See the Usage Notes below for a option listing."
621    )]
622    #[cfg_attr(
623        all(feature = "lt2024_1", not(feature = "lt2015_1")),
624        doc = "options. See Usage Notes for an option listing."
625    )]
626    #[cfg_attr(
627        not(feature = "lt2024_1"),
628        doc = "options. See Usage notes for an option listing."
629    )]
630    pub fn set_diff_options(&mut self, v: impl Into<DiffOptions>) -> &mut Self {
631        self.mode.diff_options = Some(v.into());
632        self
633    }
634
635    /// # Description
636    ///
637    /// -a
638    ///
639    /// For text files only (ignores binary files):
640    ///
641    /// - For shelved files, shows the content for "open for add" (pending) files.
642    /// - For submitted files, shows the content of added files.
643    #[cfg(not(feature = "lt2017_2"))]
644    pub fn get_display_added_text_content(&self) -> bool {
645        self.mode.display_added_text_content
646    }
647
648    /// # Description
649    ///
650    /// -a
651    ///
652    /// For text files only (ignores binary files):
653    ///
654    /// - For shelved files, shows the content for "open for add" (pending) files.
655    /// - For submitted files, shows the content of added files.
656    #[cfg(not(feature = "lt2017_2"))]
657    pub fn set_display_added_text_content(&mut self, v: bool) -> &mut Self {
658        self.mode.display_added_text_content = v;
659        self
660    }
661}
662
663impl<M: ExclusiveOption> SubCommand for Describe<M> {
664    fn name(&self) -> &str {
665        "describe"
666    }
667
668    fn inject_local_args(&self, command: &mut Command) {
669        self.mode.inject_args(command);
670
671        if self.force_restricted {
672            command.arg("-f");
673        }
674
675        #[cfg(not(feature = "lt2015_2"))]
676        if self.use_identity {
677            command.arg("-I");
678        }
679
680        #[cfg(not(feature = "lt2017_1"))]
681        if let Some(max) = self.limit {
682            command.arg("-m").arg(max.to_string());
683        }
684
685        if self.use_original_number {
686            command.arg("-O");
687        }
688
689        if self.include_shelved_files {
690            command.arg("-S");
691        }
692    }
693
694    fn global_opts(&self) -> Option<&GlobalOpts> {
695        Some(&self.global_opts)
696    }
697}
698
699#[cfg(test)]
700mod tests {
701    use super::*;
702    use crate::cmd::DiffOptionsBuilder;
703    use crate::cmd::args_of;
704
705    /// Dry-run checks of the assembled `p4 describe` command line; no process
706    /// is spawned.
707    #[test]
708    fn without_options() {
709        let describe = Describe::new("p4", GlobalOpts::new());
710
711        assert_eq!(args_of(&describe.setup_command("p4")), ["describe"]);
712    }
713
714    #[test]
715    fn unselected_with_generic_options() {
716        let mut describe = Describe::new("p4", GlobalOpts::new());
717        describe.set_force_restricted(true);
718        #[cfg(not(feature = "lt2015_2"))]
719        describe.set_use_identity(true);
720        #[cfg(not(feature = "lt2017_1"))]
721        describe.set_limit(2);
722        describe
723            .set_use_original_number(true)
724            .set_include_shelved_files(true);
725
726        let mut expected = vec!["describe"];
727        expected.push("-f");
728        #[cfg(not(feature = "lt2015_2"))]
729        expected.push("-I");
730        #[cfg(not(feature = "lt2017_1"))]
731        expected.extend(["-m", "2"]);
732        expected.extend(["-O", "-S"]);
733
734        assert_eq!(args_of(&describe.setup_command("p4")), expected);
735    }
736
737    #[test]
738    fn short_summary_mode() {
739        let describe = Describe::new("p4", GlobalOpts::new())
740            .force_restricted(true)
741            .short_summary_output();
742
743        assert_eq!(
744            args_of(&describe.setup_command("p4")),
745            ["describe", "-s", "-f"]
746        );
747    }
748
749    #[test]
750    fn mode_transition_preserves_generic_options() {
751        let mut describe = Describe::new("p4", GlobalOpts::new());
752        describe.set_force_restricted(true);
753        #[cfg(not(feature = "lt2017_1"))]
754        describe.set_limit(3);
755
756        let describe = describe.short_summary_output().include_shelved_files(true);
757
758        let mut expected = vec!["describe", "-s", "-f"];
759        #[cfg(not(feature = "lt2017_1"))]
760        expected.extend(["-m", "3"]);
761        expected.push("-S");
762
763        assert_eq!(args_of(&describe.setup_command("p4")), expected);
764    }
765
766    #[test]
767    fn detail_diff_mode_via_diff_options() {
768        let describe =
769            Describe::new("p4", GlobalOpts::new()).diff_options(DiffOptionsBuilder::unified(None));
770
771        assert!(describe.get_diff_options().is_some());
772        assert_eq!(args_of(&describe.setup_command("p4")), ["describe", "-du"]);
773    }
774
775    #[test]
776    fn detail_diff_mode_set_diff_options() {
777        let mut describe =
778            Describe::new("p4", GlobalOpts::new()).diff_options(DiffOptionsBuilder::unified(None));
779        describe.set_diff_options(DiffOptionsBuilder::summary());
780
781        assert_eq!(
782            describe.get_diff_options(),
783            Some(&DiffOptions::from(DiffOptionsBuilder::summary()))
784        );
785        assert_eq!(args_of(&describe.setup_command("p4")), ["describe", "-ds"]);
786    }
787
788    #[cfg(not(feature = "lt2017_2"))]
789    #[test]
790    fn detail_diff_mode_via_display_added_text_content() {
791        let describe = Describe::new("p4", GlobalOpts::new()).display_added_text_content(true);
792
793        assert!(describe.get_display_added_text_content());
794        assert_eq!(describe.get_diff_options(), None);
795        assert_eq!(args_of(&describe.setup_command("p4")), ["describe", "-a"]);
796    }
797
798    #[cfg(not(feature = "lt2017_2"))]
799    #[test]
800    fn detail_diff_mode_combines_a_and_d() {
801        let mut describe = Describe::new("p4", GlobalOpts::new()).display_added_text_content(true);
802        describe.set_diff_options(DiffOptionsBuilder::unified(None));
803
804        assert_eq!(
805            args_of(&describe.setup_command("p4")),
806            ["describe", "-a", "-du"]
807        );
808    }
809
810    #[cfg(not(feature = "lt2017_1"))]
811    #[test]
812    fn limit_is_injected_as_separate_args() {
813        let mut describe = Describe::new("p4", GlobalOpts::new());
814        describe.set_limit(5);
815
816        assert_eq!(
817            args_of(&describe.setup_command("p4")),
818            ["describe", "-m", "5"]
819        );
820    }
821}