Skip to main content

perforce_cli/cmd/
changes.rs

1use std::{
2    ffi::OsStr,
3    path::PathBuf,
4    process::{Child, Stdio},
5};
6
7use super::{ExclusiveOption, SubCommand, Unselected};
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12/// `-l`: full text of each changelist description.
13#[derive(Debug, Clone, Copy, Default)]
14pub struct FullDescription;
15
16impl ExclusiveOption for FullDescription {
17    fn inject_args(&self, command: &mut std::process::Command) {
18        command.arg("-l");
19    }
20}
21
22/// `-L`: full text truncated at 250 characters.
23#[derive(Debug, Clone, Copy, Default)]
24pub struct TruncatedDescription;
25
26impl ExclusiveOption for TruncatedDescription {
27    fn inject_args(&self, command: &mut std::process::Command) {
28        command.arg("-L");
29    }
30}
31
32/// User filter for the `-u` / `--me` options.
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum User {
35    /// List only changes made from the named user (`-u user`).
36    User(String),
37    /// Equivalent to `-u $P4USER` (`--me`).
38    #[cfg(not(feature = "lt2016_1"))]
39    Me,
40}
41
42/// Status of a changelist as accepted by the `-s` option.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum Status {
45    /// Pending changelists.
46    Pending,
47    /// Submitted changelists.
48    Submitted,
49    /// Shelved changelists.
50    Shelved,
51}
52
53impl Status {
54    /// Returns the command-line representation of this status.
55    pub fn as_str(&self) -> &'static str {
56        match self {
57            Status::Pending => "pending",
58            Status::Submitted => "submitted",
59            Status::Shelved => "shelved",
60        }
61    }
62}
63
64#[cfg_attr(
65    feature = "lt2015_2",
66    doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [-m max] [-s status] [-u user] [file[RevRange] ...]`"
67)]
68#[cfg_attr(
69    all(feature = "lt2016_1", not(feature = "lt2015_2")),
70    doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-s status] [-u user][file[RevRange] ...]`"
71)]
72#[cfg_attr(
73    all(feature = "lt2017_2", not(feature = "lt2016_1")),
74    doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-s status] [-u user | --me][file[RevRange] ...]`"
75)]
76#[cfg_attr(
77    all(feature = "lt2022_2", not(feature = "lt2017_2")),
78    doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-r] [-s status] [-u user | --me] [file[RevRange] ...]`"
79)]
80#[cfg_attr(
81    not(feature = "lt2022_2"),
82    doc = "`p4 [g-opts] changes [-i -t -l -L -f] [-c client] [ -e changelist#][-m max] [-r] [-s status] [-u user | --me] [file[RevRange] ...] [--stream | --nostream]`"
83)]
84///
85/// List submitted and pending changelists.
86///
87/// The command `p4 changelists` is an alias for `p4 changes`.
88///
89/// The `L` type parameter tracks the `-l` / `-L` long output mode at
90/// compile time; see [`FullDescription`], [`TruncatedDescription`],
91/// [`Self::long_output_full`], and [`Self::long_output_truncated`].
92#[derive(Debug, Clone, Default)]
93pub struct Changes<L = Unselected> {
94    bin: PathBuf,
95
96    global_opts: GlobalOpts,
97
98    #[cfg(not(feature = "lt2015_2"))]
99    min_change_list: Option<String>,
100
101    include_restricted: bool,
102
103    include_integrated: bool,
104
105    include_time: bool,
106
107    long_output: L,
108
109    limit: Option<u64>,
110
111    #[cfg(not(feature = "lt2017_2"))]
112    reverse_order: bool,
113
114    filter_status: Option<Status>,
115
116    filter_users: Option<Vec<User>>,
117
118    filter_clients: Option<Vec<String>>,
119
120    #[cfg(not(feature = "lt2025_1"))]
121    client_case_insensitive: bool,
122
123    #[cfg(not(feature = "lt2022_2"))]
124    stream: Option<bool>,
125}
126
127impl Changes<Unselected> {
128    /// Creates a new `p4 changes` 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            ..Self::default()
136        }
137    }
138
139    /// # Description
140    ///
141    /// `-l`
142    ///
143    /// List long output, with the full text of each changelist description.
144    ///
145    /// Transitions this command to the [`FullDescription`] state.
146    pub fn long_output_full(self) -> Changes<FullDescription> {
147        Changes {
148            bin: self.bin,
149            global_opts: self.global_opts,
150            #[cfg(not(feature = "lt2015_2"))]
151            min_change_list: self.min_change_list,
152            include_restricted: self.include_restricted,
153            include_integrated: self.include_integrated,
154            include_time: self.include_time,
155            long_output: FullDescription,
156            limit: self.limit,
157            #[cfg(not(feature = "lt2017_2"))]
158            reverse_order: self.reverse_order,
159            filter_status: self.filter_status,
160            filter_users: self.filter_users,
161            filter_clients: self.filter_clients,
162            #[cfg(not(feature = "lt2025_1"))]
163            client_case_insensitive: self.client_case_insensitive,
164            #[cfg(not(feature = "lt2022_2"))]
165            stream: self.stream,
166        }
167    }
168
169    /// # Description
170    ///
171    /// `-L`
172    ///
173    /// List long output, with the full text of each changelist description
174    /// truncated at 250 characters.
175    ///
176    /// Transitions this command to the [`TruncatedDescription`] state.
177    pub fn long_output_truncated(self) -> Changes<TruncatedDescription> {
178        Changes {
179            bin: self.bin,
180            global_opts: self.global_opts,
181            #[cfg(not(feature = "lt2015_2"))]
182            min_change_list: self.min_change_list,
183            include_restricted: self.include_restricted,
184            include_integrated: self.include_integrated,
185            include_time: self.include_time,
186            long_output: TruncatedDescription,
187            limit: self.limit,
188            #[cfg(not(feature = "lt2017_2"))]
189            reverse_order: self.reverse_order,
190            filter_status: self.filter_status,
191            filter_users: self.filter_users,
192            filter_clients: self.filter_clients,
193            #[cfg(not(feature = "lt2025_1"))]
194            client_case_insensitive: self.client_case_insensitive,
195            #[cfg(not(feature = "lt2022_2"))]
196            stream: self.stream,
197        }
198    }
199}
200
201impl<L: ExclusiveOption, S, I> ParameterizedSpawn<(S,)> for Changes<L>
202where
203    S: IntoIterator<Item = I>,
204    I: AsRef<OsStr>,
205{
206    type Output = Child;
207    type Error = std::io::Error;
208
209    /// Spawns `p4 changes` for the given files as a child process with piped
210    /// standard output and error streams; use the returned [`Child`] handle
211    /// to wait for it or interact with it.
212    ///
213    /// If files are specified, only changelists that affect those files are
214    /// listed. Pass an empty slice to list all changelists.
215    fn spawn_with(&mut self, (files,): (S,)) -> Result<Self::Output, Self::Error> {
216        self.setup_command(&self.bin)
217            .args(files)
218            .stdout(Stdio::piped())
219            .stderr(Stdio::piped())
220            .spawn()
221    }
222}
223
224impl<L: ExclusiveOption> Changes<L> {
225    /// # Description
226    ///
227    /// g-opts
228    ///
229    #[cfg_attr(
230        feature = "lt2014_2",
231        doc = "See the [Global Options](GlobalOpts) section."
232    )]
233    #[cfg_attr(
234        all(feature = "lt2015_1", not(feature = "lt2014_2")),
235        doc = "See the [“Global Options”](GlobalOpts) section."
236    )]
237    #[cfg_attr(
238        all(feature = "lt2017_1", not(feature = "lt2015_1")),
239        doc = "See [“Global Options”](GlobalOpts)."
240    )]
241    #[cfg_attr(
242        all(feature = "lt2018_2", not(feature = "lt2017_1")),
243        doc = "See [Global Options](GlobalOpts)."
244    )]
245    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
246    pub fn get_global_opts(&self) -> &GlobalOpts {
247        &self.global_opts
248    }
249
250    /// # Description
251    ///
252    /// g-opts
253    ///
254    #[cfg_attr(
255        feature = "lt2014_2",
256        doc = "See the [Global Options](GlobalOpts) section."
257    )]
258    #[cfg_attr(
259        all(feature = "lt2015_1", not(feature = "lt2014_2")),
260        doc = "See the [“Global Options”](GlobalOpts) section."
261    )]
262    #[cfg_attr(
263        all(feature = "lt2017_1", not(feature = "lt2015_1")),
264        doc = "See [“Global Options”](GlobalOpts)."
265    )]
266    #[cfg_attr(
267        all(feature = "lt2018_2", not(feature = "lt2017_1")),
268        doc = "See [Global Options](GlobalOpts)."
269    )]
270    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
271    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
272        self.global_opts = v;
273        self
274    }
275
276    /// # Description
277    ///
278    /// g-opts
279    ///
280    #[cfg_attr(
281        feature = "lt2014_2",
282        doc = "See the [Global Options](GlobalOpts) section."
283    )]
284    #[cfg_attr(
285        all(feature = "lt2015_1", not(feature = "lt2014_2")),
286        doc = "See the [“Global Options”](GlobalOpts) section."
287    )]
288    #[cfg_attr(
289        all(feature = "lt2017_1", not(feature = "lt2015_1")),
290        doc = "See [“Global Options”](GlobalOpts)."
291    )]
292    #[cfg_attr(
293        all(feature = "lt2018_2", not(feature = "lt2017_1")),
294        doc = "See [Global Options](GlobalOpts)."
295    )]
296    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
297    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
298        self.global_opts = v;
299        self
300    }
301
302    /// # Description
303    ///
304    /// `-c client`
305    ///
306    /// List only changes made from the named client workspace. This option
307    /// can be repeated to filter for multiple clients.
308    pub fn get_filter_clients(&self) -> Option<&[String]> {
309        self.filter_clients.as_deref()
310    }
311
312    /// # Description
313    ///
314    /// `-c client`
315    ///
316    /// List only changes made from the named client workspace. This option
317    /// can be repeated to filter for multiple clients.
318    pub fn set_filter_client(&mut self, v: impl Into<String>) -> &mut Self {
319        self.filter_clients
320            .get_or_insert_with(Vec::new)
321            .push(v.into());
322        self
323    }
324
325    /// # Description
326    ///
327    /// `-c client`
328    ///
329    /// List only changes made from the named client workspace. This option
330    /// can be repeated to filter for multiple clients.
331    pub fn filter_client(mut self, v: impl Into<String>) -> Self {
332        self.filter_clients
333            .get_or_insert_with(Vec::new)
334            .push(v.into());
335        self
336    }
337
338    /// # Description
339    ///
340    /// `-e changelist#`
341    ///
342    /// Display only changes where the changelist number is equal to, or
343    /// higher than, the specified changelist number.
344    #[cfg(not(feature = "lt2015_2"))]
345    pub fn get_min_change_list(&self) -> Option<&String> {
346        self.min_change_list.as_ref()
347    }
348
349    /// # Description
350    ///
351    /// `-e changelist#`
352    ///
353    /// Display only changes where the changelist number is equal to, or
354    /// higher than, the specified changelist number.
355    #[cfg(not(feature = "lt2015_2"))]
356    pub fn set_min_change_list(&mut self, v: impl Into<String>) -> &mut Self {
357        self.min_change_list = Some(v.into());
358        self
359    }
360
361    /// # Description
362    ///
363    /// `-e changelist#`
364    ///
365    /// Display only changes where the changelist number is equal to, or
366    /// higher than, the specified changelist number.
367    #[cfg(not(feature = "lt2015_2"))]
368    pub fn min_change_list(mut self, v: impl Into<String>) -> Self {
369        self.min_change_list = Some(v.into());
370        self
371    }
372
373    /// # Description
374    ///
375    /// `-f`
376    ///
377    /// View restricted changes (requires admin permission).
378    pub fn get_include_restricted(&self) -> bool {
379        self.include_restricted
380    }
381
382    /// # Description
383    ///
384    /// `-f`
385    ///
386    /// View restricted changes (requires admin permission).
387    pub fn set_include_restricted(&mut self, v: bool) -> &mut Self {
388        self.include_restricted = v;
389        self
390    }
391
392    /// # Description
393    ///
394    /// `-f`
395    ///
396    /// View restricted changes (requires admin permission).
397    pub fn include_restricted(mut self, v: bool) -> Self {
398        self.include_restricted = v;
399        self
400    }
401
402    /// # Description
403    ///
404    /// `-i`
405    ///
406    /// Include changelists that affected files that were integrated with the
407    /// specified files.
408    pub fn get_include_integrated(&self) -> bool {
409        self.include_integrated
410    }
411
412    /// # Description
413    ///
414    /// `-i`
415    ///
416    /// Include changelists that affected files that were integrated with the
417    /// specified files.
418    pub fn set_include_integrated(&mut self, v: bool) -> &mut Self {
419        self.include_integrated = v;
420        self
421    }
422
423    /// # Description
424    ///
425    /// `-i`
426    ///
427    /// Include changelists that affected files that were integrated with the
428    /// specified files.
429    pub fn include_integrated(mut self, v: bool) -> Self {
430        self.include_integrated = v;
431        self
432    }
433
434    /// # Description
435    ///
436    /// `-m max`
437    ///
438    /// List only the highest numbered `max` changes.
439    pub fn get_limit(&self) -> Option<u64> {
440        self.limit
441    }
442
443    /// # Description
444    ///
445    /// `-m max`
446    ///
447    /// List only the highest numbered `max` changes.
448    pub fn set_limit(&mut self, v: u64) -> &mut Self {
449        self.limit = Some(v);
450        self
451    }
452
453    /// # Description
454    ///
455    /// `-m max`
456    ///
457    /// List only the highest numbered `max` changes.
458    pub fn limit(mut self, v: u64) -> Self {
459        self.limit = Some(v);
460        self
461    }
462
463    /// # Description
464    ///
465    /// `-r`
466    ///
467    /// Reverse the order of the list, earliest first instead of most recent
468    /// first.
469    #[cfg(not(feature = "lt2017_2"))]
470    pub fn get_reverse_order(&self) -> bool {
471        self.reverse_order
472    }
473
474    /// # Description
475    ///
476    /// `-r`
477    ///
478    /// Reverse the order of the list, earliest first instead of most recent
479    /// first.
480    #[cfg(not(feature = "lt2017_2"))]
481    pub fn set_reverse_order(&mut self, v: bool) -> &mut Self {
482        self.reverse_order = v;
483        self
484    }
485
486    /// # Description
487    ///
488    /// `-r`
489    ///
490    /// Reverse the order of the list, earliest first instead of most recent
491    /// first.
492    #[cfg(not(feature = "lt2017_2"))]
493    pub fn reverse_order(mut self, v: bool) -> Self {
494        self.reverse_order = v;
495        self
496    }
497
498    /// # Description
499    ///
500    /// `-s status`
501    ///
502    /// Limit the list to the changelists with the specified status:
503    /// `pending`, `submitted`, or `shelved`.
504    pub fn get_status(&self) -> Option<Status> {
505        self.filter_status
506    }
507
508    /// # Description
509    ///
510    /// `-s status`
511    ///
512    /// Limit the list to the changelists with the specified status:
513    /// `pending`, `submitted`, or `shelved`.
514    pub fn set_status(&mut self, v: Status) -> &mut Self {
515        self.filter_status = Some(v);
516        self
517    }
518
519    /// # Description
520    ///
521    /// `-s status`
522    ///
523    /// Limit the list to the changelists with the specified status:
524    /// `pending`, `submitted`, or `shelved`.
525    pub fn status(mut self, v: Status) -> Self {
526        self.filter_status = Some(v);
527        self
528    }
529
530    /// # Description
531    ///
532    /// `-t`
533    ///
534    /// Display the time as well as the date of each change.
535    pub fn get_include_time(&self) -> bool {
536        self.include_time
537    }
538
539    /// # Description
540    ///
541    /// `-t`
542    ///
543    /// Display the time as well as the date of each change.
544    pub fn set_include_time(&mut self, v: bool) -> &mut Self {
545        self.include_time = v;
546        self
547    }
548
549    /// # Description
550    ///
551    /// `-t`
552    ///
553    /// Display the time as well as the date of each change.
554    pub fn include_time(mut self, v: bool) -> Self {
555        self.include_time = v;
556        self
557    }
558
559    /// # Description
560    ///
561    /// `-u user` / `--me`
562    ///
563    /// List only changes made from the named user, or, with
564    /// [`User::Me`], the current user (equivalent to `-u $P4USER`).
565    /// This option can be repeated to filter for multiple users.
566    pub fn get_filter_users(&self) -> Option<&[User]> {
567        self.filter_users.as_deref()
568    }
569
570    /// # Description
571    ///
572    /// `-u user`
573    ///
574    /// List only changes made from the named user. This option can be
575    /// repeated to filter for multiple users.
576    pub fn set_filter_user(&mut self, v: impl Into<String>) -> &mut Self {
577        self.filter_users
578            .get_or_insert_with(Vec::new)
579            .push(User::User(v.into()));
580        self
581    }
582
583    /// # Description
584    ///
585    /// `-u user`
586    ///
587    /// List only changes made from the named user. This option can be
588    /// repeated to filter for multiple users.
589    pub fn filter_user(mut self, v: impl Into<String>) -> Self {
590        self.filter_users
591            .get_or_insert_with(Vec::new)
592            .push(User::User(v.into()));
593        self
594    }
595
596    /// # Description
597    ///
598    /// `--me`
599    ///
600    /// Equivalent to `-u $P4USER`.
601    #[cfg(not(feature = "lt2016_1"))]
602    pub fn set_filter_me(&mut self) -> &mut Self {
603        self.filter_users
604            .get_or_insert_with(Vec::new)
605            .push(User::Me);
606        self
607    }
608
609    /// # Description
610    ///
611    /// `--me`
612    ///
613    /// Equivalent to `-u $P4USER`.
614    #[cfg(not(feature = "lt2016_1"))]
615    pub fn filter_me(mut self) -> Self {
616        self.filter_users
617            .get_or_insert_with(Vec::new)
618            .push(User::Me);
619        self
620    }
621
622    /// # Description
623    ///
624    /// `--client-case-insensitive`
625    ///
626    /// Makes the `-c client` search pattern case-insensitive, even on a
627    /// case-sensitive server.
628    #[cfg(not(feature = "lt2025_1"))]
629    pub fn get_client_case_insensitive(&self) -> bool {
630        self.client_case_insensitive
631    }
632
633    /// # Description
634    ///
635    /// `--client-case-insensitive`
636    ///
637    /// Makes the `-c client` search pattern case-insensitive, even on a
638    /// case-sensitive server.
639    #[cfg(not(feature = "lt2025_1"))]
640    pub fn set_client_case_insensitive(&mut self, v: bool) -> &mut Self {
641        self.client_case_insensitive = v;
642        self
643    }
644
645    /// # Description
646    ///
647    /// `--client-case-insensitive`
648    ///
649    /// Makes the `-c client` search pattern case-insensitive, even on a
650    /// case-sensitive server.
651    #[cfg(not(feature = "lt2025_1"))]
652    pub fn client_case_insensitive(mut self, v: bool) -> Self {
653        self.client_case_insensitive = v;
654        self
655    }
656
657    /// # Description
658    ///
659    /// `--stream` / `--nostream`
660    ///
661    /// With `true`, display only changes that contain a stream spec
662    /// (`--stream`). With `false`, display only changes that do not
663    /// contain a stream spec (`--nostream`).
664    #[cfg(not(feature = "lt2022_2"))]
665    pub fn get_stream(&self) -> Option<bool> {
666        self.stream
667    }
668
669    /// # Description
670    ///
671    /// `--stream` / `--nostream`
672    ///
673    /// With `true`, display only changes that contain a stream spec
674    /// (`--stream`). With `false`, display only changes that do not
675    /// contain a stream spec (`--nostream`).
676    #[cfg(not(feature = "lt2022_2"))]
677    pub fn set_stream(&mut self, v: bool) -> &mut Self {
678        self.stream = Some(v);
679        self
680    }
681
682    /// # Description
683    ///
684    /// `--stream` / `--nostream`
685    ///
686    /// With `true`, display only changes that contain a stream spec
687    /// (`--stream`). With `false`, display only changes that do not
688    /// contain a stream spec (`--nostream`).
689    #[cfg(not(feature = "lt2022_2"))]
690    pub fn stream(mut self, v: bool) -> Self {
691        self.stream = Some(v);
692        self
693    }
694}
695
696impl<L: ExclusiveOption> SubCommand for Changes<L> {
697    fn name(&self) -> &str {
698        "changes"
699    }
700
701    fn inject_local_args(&self, command: &mut std::process::Command) {
702        if let Some(ref clients) = self.filter_clients {
703            for client in clients {
704                command.arg("-c").arg(client);
705            }
706        }
707        #[cfg(not(feature = "lt2025_1"))]
708        if self.client_case_insensitive {
709            command.arg("--client-case-insensitive");
710        }
711        #[cfg(not(feature = "lt2015_2"))]
712        if let Some(ref min_change) = self.min_change_list {
713            command.arg("-e").arg(min_change);
714        }
715        if self.include_restricted {
716            command.arg("-f");
717        }
718        if self.include_integrated {
719            command.arg("-i");
720        }
721        self.long_output.inject_args(command);
722        if let Some(max) = self.limit {
723            command.arg("-m").arg(max.to_string());
724        }
725        #[cfg(not(feature = "lt2017_2"))]
726        if self.reverse_order {
727            command.arg("-r");
728        }
729        if let Some(status) = self.filter_status {
730            command.arg("-s").arg(status.as_str());
731        }
732        if self.include_time {
733            command.arg("-t");
734        }
735        if let Some(ref users) = self.filter_users {
736            for user in users {
737                match user {
738                    User::User(name) => {
739                        command.arg("-u").arg(name);
740                    }
741                    #[cfg(not(feature = "lt2016_1"))]
742                    User::Me => {
743                        command.arg("--me");
744                    }
745                }
746            }
747        }
748        #[cfg(not(feature = "lt2022_2"))]
749        if let Some(stream) = self.stream {
750            if stream {
751                command.arg("--stream");
752            } else {
753                command.arg("--nostream");
754            }
755        }
756    }
757
758    fn global_opts(&self) -> Option<&GlobalOpts> {
759        Some(&self.global_opts)
760    }
761}
762
763#[cfg(test)]
764mod tests {
765    use super::*;
766    use crate::cmd::args_of;
767
768    #[test]
769    fn without_options() {
770        let changes = Changes::new("p4", GlobalOpts::default());
771        let cmd = changes.setup_command("p4");
772        assert_eq!(args_of(&cmd), vec!["changes"]);
773    }
774
775    #[test]
776    fn with_files() {
777        let changes = Changes::new("p4", GlobalOpts::default());
778        let mut cmd = changes.setup_command("p4");
779        cmd.arg("//depot/project/...");
780        assert_eq!(args_of(&cmd), vec!["changes", "//depot/project/..."]);
781    }
782
783    #[test]
784    fn client() {
785        let changes = Changes::new("p4", GlobalOpts::default()).filter_client("eds_elm");
786        let cmd = changes.setup_command("p4");
787        assert_eq!(args_of(&cmd), vec!["changes", "-c", "eds_elm"]);
788    }
789
790    #[test]
791    fn multiple_clients() {
792        let changes = Changes::new("p4", GlobalOpts::default())
793            .filter_client("eds_elm")
794            .filter_client("build_ws");
795        let cmd = changes.setup_command("p4");
796        assert_eq!(
797            args_of(&cmd),
798            vec!["changes", "-c", "eds_elm", "-c", "build_ws"]
799        );
800    }
801
802    #[cfg(not(feature = "lt2015_2"))]
803    #[test]
804    fn min_change_list() {
805        let changes = Changes::new("p4", GlobalOpts::default()).min_change_list("800");
806        let cmd = changes.setup_command("p4");
807        assert_eq!(args_of(&cmd), vec!["changes", "-e", "800"]);
808    }
809
810    #[test]
811    fn include_restricted() {
812        let changes = Changes::new("p4", GlobalOpts::default()).include_restricted(true);
813        let cmd = changes.setup_command("p4");
814        assert_eq!(args_of(&cmd), vec!["changes", "-f"]);
815    }
816
817    #[test]
818    fn include_integrated() {
819        let changes = Changes::new("p4", GlobalOpts::default()).include_integrated(true);
820        let cmd = changes.setup_command("p4");
821        assert_eq!(args_of(&cmd), vec!["changes", "-i"]);
822    }
823
824    #[test]
825    fn long_output_full() {
826        let changes = Changes::new("p4", GlobalOpts::default()).long_output_full();
827        let cmd = changes.setup_command("p4");
828        assert_eq!(args_of(&cmd), vec!["changes", "-l"]);
829    }
830
831    #[test]
832    fn long_output_truncated() {
833        let changes = Changes::new("p4", GlobalOpts::default()).long_output_truncated();
834        let cmd = changes.setup_command("p4");
835        assert_eq!(args_of(&cmd), vec!["changes", "-L"]);
836    }
837
838    #[test]
839    fn limit() {
840        let changes = Changes::new("p4", GlobalOpts::default()).limit(5);
841        let cmd = changes.setup_command("p4");
842        assert_eq!(args_of(&cmd), vec!["changes", "-m", "5"]);
843    }
844
845    #[cfg(not(feature = "lt2017_2"))]
846    #[test]
847    fn reverse_order() {
848        let changes = Changes::new("p4", GlobalOpts::default()).reverse_order(true);
849        let cmd = changes.setup_command("p4");
850        assert_eq!(args_of(&cmd), vec!["changes", "-r"]);
851    }
852
853    #[test]
854    fn status_pending() {
855        let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Pending);
856        let cmd = changes.setup_command("p4");
857        assert_eq!(args_of(&cmd), vec!["changes", "-s", "pending"]);
858    }
859
860    #[test]
861    fn status_submitted() {
862        let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Submitted);
863        let cmd = changes.setup_command("p4");
864        assert_eq!(args_of(&cmd), vec!["changes", "-s", "submitted"]);
865    }
866
867    #[test]
868    fn status_shelved() {
869        let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Shelved);
870        let cmd = changes.setup_command("p4");
871        assert_eq!(args_of(&cmd), vec!["changes", "-s", "shelved"]);
872    }
873
874    #[test]
875    fn include_time() {
876        let changes = Changes::new("p4", GlobalOpts::default()).include_time(true);
877        let cmd = changes.setup_command("p4");
878        assert_eq!(args_of(&cmd), vec!["changes", "-t"]);
879    }
880
881    #[test]
882    fn user_name() {
883        let changes = Changes::new("p4", GlobalOpts::default()).filter_user("edk");
884        let cmd = changes.setup_command("p4");
885        assert_eq!(args_of(&cmd), vec!["changes", "-u", "edk"]);
886    }
887
888    #[test]
889    fn multiple_users() {
890        let changes = Changes::new("p4", GlobalOpts::default())
891            .filter_user("maria")
892            .filter_user("edk");
893        let cmd = changes.setup_command("p4");
894        assert_eq!(args_of(&cmd), vec!["changes", "-u", "maria", "-u", "edk"]);
895    }
896
897    #[cfg(not(feature = "lt2016_1"))]
898    #[test]
899    fn user_me() {
900        let changes = Changes::new("p4", GlobalOpts::default()).filter_me();
901        let cmd = changes.setup_command("p4");
902        assert_eq!(args_of(&cmd), vec!["changes", "--me"]);
903    }
904
905    #[cfg(not(feature = "lt2016_1"))]
906    #[test]
907    fn multiple_users_with_me() {
908        let changes = Changes::new("p4", GlobalOpts::default())
909            .filter_user("maria")
910            .filter_me();
911        let cmd = changes.setup_command("p4");
912        assert_eq!(args_of(&cmd), vec!["changes", "-u", "maria", "--me"]);
913    }
914
915    #[cfg(not(feature = "lt2025_1"))]
916    #[test]
917    fn client_case_insensitive() {
918        let changes = Changes::new("p4", GlobalOpts::default())
919            .filter_client("eds_elm")
920            .client_case_insensitive(true);
921        let cmd = changes.setup_command("p4");
922        assert_eq!(
923            args_of(&cmd),
924            vec!["changes", "-c", "eds_elm", "--client-case-insensitive"]
925        );
926    }
927
928    #[cfg(not(feature = "lt2022_2"))]
929    #[test]
930    fn stream_spec() {
931        let changes = Changes::new("p4", GlobalOpts::default()).stream(true);
932        let cmd = changes.setup_command("p4");
933        assert_eq!(args_of(&cmd), vec!["changes", "--stream"]);
934    }
935
936    #[cfg(not(feature = "lt2022_2"))]
937    #[test]
938    fn no_stream_spec() {
939        let changes = Changes::new("p4", GlobalOpts::default()).stream(false);
940        let cmd = changes.setup_command("p4");
941        assert_eq!(args_of(&cmd), vec!["changes", "--nostream"]);
942    }
943
944    #[test]
945    fn all_options_order() {
946        let changes = Changes::new("p4", GlobalOpts::default())
947            .filter_client("eds_elm")
948            .include_restricted(true)
949            .include_integrated(true)
950            .long_output_full()
951            .limit(5)
952            .status(Status::Submitted)
953            .include_time(true)
954            .filter_user("edk");
955        #[cfg(not(feature = "lt2015_2"))]
956        let changes = changes.min_change_list("800");
957        #[cfg(not(feature = "lt2017_2"))]
958        let changes = changes.reverse_order(true);
959        #[cfg(not(feature = "lt2022_2"))]
960        let changes = changes.stream(true);
961        let cmd = changes.setup_command("p4");
962        let mut expected = vec!["changes", "-c", "eds_elm"];
963        #[cfg(not(feature = "lt2015_2"))]
964        expected.extend(["-e", "800"]);
965        expected.extend(["-f", "-i", "-l", "-m", "5"]);
966        #[cfg(not(feature = "lt2017_2"))]
967        expected.push("-r");
968        expected.extend(["-s", "submitted", "-t", "-u", "edk"]);
969        #[cfg(not(feature = "lt2022_2"))]
970        expected.push("--stream");
971        assert_eq!(args_of(&cmd), expected);
972    }
973
974    #[test]
975    fn long_output_full_preserves_other_options() {
976        let changes = Changes::new("p4", GlobalOpts::default())
977            .include_restricted(true)
978            .include_time(true)
979            .long_output_full();
980        let cmd = changes.setup_command("p4");
981        assert_eq!(args_of(&cmd), vec!["changes", "-f", "-l", "-t"]);
982    }
983}