1use std::{
2 ffi::OsStr,
3 path::{Path, PathBuf},
4 process::{Child, Stdio},
5};
6
7use super::{ExclusiveOption, SubCommand, Unselected};
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12#[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#[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#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum User {
35 User(String),
37 #[cfg(not(feature = "lt2016_1"))]
39 Me,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum Status {
45 Pending,
47 Submitted,
49 Shelved,
51}
52
53impl Status {
54 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#[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 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 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 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 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 pub fn get_bin(&self) -> &Path {
227 &self.bin
228 }
229
230 pub fn set_bin(&mut self, bin: impl Into<PathBuf>) -> &mut Self {
232 self.bin = bin.into();
233 self
234 }
235
236 pub fn bin(mut self, bin: impl Into<PathBuf>) -> Self {
238 self.set_bin(bin);
239 self
240 }
241
242 #[cfg_attr(
247 feature = "lt2014_2",
248 doc = "See the [Global Options](GlobalOpts) section."
249 )]
250 #[cfg_attr(
251 all(feature = "lt2015_1", not(feature = "lt2014_2")),
252 doc = "See the [“Global Options”](GlobalOpts) section."
253 )]
254 #[cfg_attr(
255 all(feature = "lt2017_1", not(feature = "lt2015_1")),
256 doc = "See [“Global Options”](GlobalOpts)."
257 )]
258 #[cfg_attr(
259 all(feature = "lt2018_2", not(feature = "lt2017_1")),
260 doc = "See [Global Options](GlobalOpts)."
261 )]
262 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
263 pub fn get_global_opts(&self) -> &GlobalOpts {
264 &self.global_opts
265 }
266
267 #[cfg_attr(
272 feature = "lt2014_2",
273 doc = "See the [Global Options](GlobalOpts) section."
274 )]
275 #[cfg_attr(
276 all(feature = "lt2015_1", not(feature = "lt2014_2")),
277 doc = "See the [“Global Options”](GlobalOpts) section."
278 )]
279 #[cfg_attr(
280 all(feature = "lt2017_1", not(feature = "lt2015_1")),
281 doc = "See [“Global Options”](GlobalOpts)."
282 )]
283 #[cfg_attr(
284 all(feature = "lt2018_2", not(feature = "lt2017_1")),
285 doc = "See [Global Options](GlobalOpts)."
286 )]
287 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
288 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
289 self.global_opts = v;
290 self
291 }
292
293 #[cfg_attr(
298 feature = "lt2014_2",
299 doc = "See the [Global Options](GlobalOpts) section."
300 )]
301 #[cfg_attr(
302 all(feature = "lt2015_1", not(feature = "lt2014_2")),
303 doc = "See the [“Global Options”](GlobalOpts) section."
304 )]
305 #[cfg_attr(
306 all(feature = "lt2017_1", not(feature = "lt2015_1")),
307 doc = "See [“Global Options”](GlobalOpts)."
308 )]
309 #[cfg_attr(
310 all(feature = "lt2018_2", not(feature = "lt2017_1")),
311 doc = "See [Global Options](GlobalOpts)."
312 )]
313 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
314 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
315 self.global_opts = v;
316 self
317 }
318
319 pub fn get_filter_clients(&self) -> Option<&[String]> {
326 self.filter_clients.as_deref()
327 }
328
329 pub fn set_filter_client(&mut self, v: impl Into<String>) -> &mut Self {
336 self.filter_clients
337 .get_or_insert_with(Vec::new)
338 .push(v.into());
339 self
340 }
341
342 pub fn filter_client(mut self, v: impl Into<String>) -> Self {
349 self.filter_clients
350 .get_or_insert_with(Vec::new)
351 .push(v.into());
352 self
353 }
354
355 #[cfg(not(feature = "lt2015_2"))]
362 pub fn get_min_change_list(&self) -> Option<&String> {
363 self.min_change_list.as_ref()
364 }
365
366 #[cfg(not(feature = "lt2015_2"))]
373 pub fn set_min_change_list(&mut self, v: impl Into<String>) -> &mut Self {
374 self.min_change_list = Some(v.into());
375 self
376 }
377
378 #[cfg(not(feature = "lt2015_2"))]
385 pub fn min_change_list(mut self, v: impl Into<String>) -> Self {
386 self.min_change_list = Some(v.into());
387 self
388 }
389
390 pub fn get_include_restricted(&self) -> bool {
396 self.include_restricted
397 }
398
399 pub fn set_include_restricted(&mut self, v: bool) -> &mut Self {
405 self.include_restricted = v;
406 self
407 }
408
409 pub fn include_restricted(mut self, v: bool) -> Self {
415 self.include_restricted = v;
416 self
417 }
418
419 pub fn get_include_integrated(&self) -> bool {
426 self.include_integrated
427 }
428
429 pub fn set_include_integrated(&mut self, v: bool) -> &mut Self {
436 self.include_integrated = v;
437 self
438 }
439
440 pub fn include_integrated(mut self, v: bool) -> Self {
447 self.include_integrated = v;
448 self
449 }
450
451 pub fn get_limit(&self) -> Option<u64> {
457 self.limit
458 }
459
460 pub fn set_limit(&mut self, v: u64) -> &mut Self {
466 self.limit = Some(v);
467 self
468 }
469
470 pub fn limit(mut self, v: u64) -> Self {
476 self.limit = Some(v);
477 self
478 }
479
480 #[cfg(not(feature = "lt2017_2"))]
487 pub fn get_reverse_order(&self) -> bool {
488 self.reverse_order
489 }
490
491 #[cfg(not(feature = "lt2017_2"))]
498 pub fn set_reverse_order(&mut self, v: bool) -> &mut Self {
499 self.reverse_order = v;
500 self
501 }
502
503 #[cfg(not(feature = "lt2017_2"))]
510 pub fn reverse_order(mut self, v: bool) -> Self {
511 self.reverse_order = v;
512 self
513 }
514
515 pub fn get_status(&self) -> Option<Status> {
522 self.filter_status
523 }
524
525 pub fn set_status(&mut self, v: Status) -> &mut Self {
532 self.filter_status = Some(v);
533 self
534 }
535
536 pub fn status(mut self, v: Status) -> Self {
543 self.filter_status = Some(v);
544 self
545 }
546
547 pub fn get_include_time(&self) -> bool {
553 self.include_time
554 }
555
556 pub fn set_include_time(&mut self, v: bool) -> &mut Self {
562 self.include_time = v;
563 self
564 }
565
566 pub fn include_time(mut self, v: bool) -> Self {
572 self.include_time = v;
573 self
574 }
575
576 #[cfg_attr(feature = "lt2016_1", doc = "-u user")]
579 #[cfg_attr(not(feature = "lt2016_1"), doc = "-u user / --me")]
580 #[cfg_attr(
582 feature = "lt2016_1",
583 doc = "List only changes made from the named user. This option can be repeated to filter for multiple users."
584 )]
585 #[cfg_attr(
586 not(feature = "lt2016_1"),
587 doc = "List only changes made from the named user, or, with [`User::Me`], the current user (equivalent to `-u $P4USER`). This option can be repeated to filter for multiple users."
588 )]
589 pub fn get_filter_users(&self) -> Option<&[User]> {
590 self.filter_users.as_deref()
591 }
592
593 pub fn set_filter_user(&mut self, v: impl Into<String>) -> &mut Self {
600 self.filter_users
601 .get_or_insert_with(Vec::new)
602 .push(User::User(v.into()));
603 self
604 }
605
606 pub fn filter_user(mut self, v: impl Into<String>) -> Self {
613 self.filter_users
614 .get_or_insert_with(Vec::new)
615 .push(User::User(v.into()));
616 self
617 }
618
619 #[cfg(not(feature = "lt2016_1"))]
625 pub fn set_filter_me(&mut self) -> &mut Self {
626 self.filter_users
627 .get_or_insert_with(Vec::new)
628 .push(User::Me);
629 self
630 }
631
632 #[cfg(not(feature = "lt2016_1"))]
638 pub fn filter_me(mut self) -> Self {
639 self.filter_users
640 .get_or_insert_with(Vec::new)
641 .push(User::Me);
642 self
643 }
644
645 #[cfg(not(feature = "lt2025_1"))]
652 pub fn get_client_case_insensitive(&self) -> bool {
653 self.client_case_insensitive
654 }
655
656 #[cfg(not(feature = "lt2025_1"))]
663 pub fn set_client_case_insensitive(&mut self, v: bool) -> &mut Self {
664 self.client_case_insensitive = v;
665 self
666 }
667
668 #[cfg(not(feature = "lt2025_1"))]
675 pub fn client_case_insensitive(mut self, v: bool) -> Self {
676 self.client_case_insensitive = v;
677 self
678 }
679
680 #[cfg(not(feature = "lt2022_2"))]
688 pub fn get_stream(&self) -> Option<bool> {
689 self.stream
690 }
691
692 #[cfg(not(feature = "lt2022_2"))]
700 pub fn set_stream(&mut self, v: bool) -> &mut Self {
701 self.stream = Some(v);
702 self
703 }
704
705 #[cfg(not(feature = "lt2022_2"))]
713 pub fn stream(mut self, v: bool) -> Self {
714 self.stream = Some(v);
715 self
716 }
717}
718
719impl<L: ExclusiveOption> SubCommand for Changes<L> {
720 fn name(&self) -> &str {
721 "changes"
722 }
723
724 fn inject_local_args(&self, command: &mut std::process::Command) {
725 if let Some(ref clients) = self.filter_clients {
726 for client in clients {
727 command.arg("-c").arg(client);
728 }
729 }
730 #[cfg(not(feature = "lt2025_1"))]
731 if self.client_case_insensitive {
732 command.arg("--client-case-insensitive");
733 }
734 #[cfg(not(feature = "lt2015_2"))]
735 if let Some(ref min_change) = self.min_change_list {
736 command.arg("-e").arg(min_change);
737 }
738 if self.include_restricted {
739 command.arg("-f");
740 }
741 if self.include_integrated {
742 command.arg("-i");
743 }
744 self.long_output.inject_args(command);
745 if let Some(max) = self.limit {
746 command.arg("-m").arg(max.to_string());
747 }
748 #[cfg(not(feature = "lt2017_2"))]
749 if self.reverse_order {
750 command.arg("-r");
751 }
752 if let Some(status) = self.filter_status {
753 command.arg("-s").arg(status.as_str());
754 }
755 if self.include_time {
756 command.arg("-t");
757 }
758 if let Some(ref users) = self.filter_users {
759 for user in users {
760 match user {
761 User::User(name) => {
762 command.arg("-u").arg(name);
763 }
764 #[cfg(not(feature = "lt2016_1"))]
765 User::Me => {
766 command.arg("--me");
767 }
768 }
769 }
770 }
771 #[cfg(not(feature = "lt2022_2"))]
772 if let Some(stream) = self.stream {
773 if stream {
774 command.arg("--stream");
775 } else {
776 command.arg("--nostream");
777 }
778 }
779 }
780
781 fn global_opts(&self) -> Option<&GlobalOpts> {
782 Some(&self.global_opts)
783 }
784}
785
786#[cfg(test)]
787mod tests {
788 use super::*;
789 use crate::cmd::args_of;
790
791 #[test]
792 fn bin_accessors_update_executable_path() {
793 let mut command = Changes::new("p4", GlobalOpts::new());
794 assert_eq!(command.get_bin(), Path::new("p4"));
795
796 command.set_bin("/usr/local/bin/p4");
797 assert_eq!(command.get_bin(), Path::new("/usr/local/bin/p4"));
798
799 let command = command.bin("p4.exe");
800 assert_eq!(command.get_bin(), Path::new("p4.exe"));
801 }
802
803 #[test]
804 fn without_options() {
805 let changes = Changes::new("p4", GlobalOpts::default());
806 let cmd = changes.setup_command("p4");
807 assert_eq!(args_of(&cmd), vec!["changes"]);
808 }
809
810 #[test]
811 fn with_files() {
812 let changes = Changes::new("p4", GlobalOpts::default());
813 let mut cmd = changes.setup_command("p4");
814 cmd.arg("//depot/project/...");
815 assert_eq!(args_of(&cmd), vec!["changes", "//depot/project/..."]);
816 }
817
818 #[test]
819 fn client() {
820 let changes = Changes::new("p4", GlobalOpts::default()).filter_client("eds_elm");
821 let cmd = changes.setup_command("p4");
822 assert_eq!(args_of(&cmd), vec!["changes", "-c", "eds_elm"]);
823 }
824
825 #[test]
826 fn multiple_clients() {
827 let changes = Changes::new("p4", GlobalOpts::default())
828 .filter_client("eds_elm")
829 .filter_client("build_ws");
830 let cmd = changes.setup_command("p4");
831 assert_eq!(
832 args_of(&cmd),
833 vec!["changes", "-c", "eds_elm", "-c", "build_ws"]
834 );
835 }
836
837 #[cfg(not(feature = "lt2015_2"))]
838 #[test]
839 fn min_change_list() {
840 let changes = Changes::new("p4", GlobalOpts::default()).min_change_list("800");
841 let cmd = changes.setup_command("p4");
842 assert_eq!(args_of(&cmd), vec!["changes", "-e", "800"]);
843 }
844
845 #[test]
846 fn include_restricted() {
847 let changes = Changes::new("p4", GlobalOpts::default()).include_restricted(true);
848 let cmd = changes.setup_command("p4");
849 assert_eq!(args_of(&cmd), vec!["changes", "-f"]);
850 }
851
852 #[test]
853 fn include_integrated() {
854 let changes = Changes::new("p4", GlobalOpts::default()).include_integrated(true);
855 let cmd = changes.setup_command("p4");
856 assert_eq!(args_of(&cmd), vec!["changes", "-i"]);
857 }
858
859 #[test]
860 fn long_output_full() {
861 let changes = Changes::new("p4", GlobalOpts::default()).long_output_full();
862 let cmd = changes.setup_command("p4");
863 assert_eq!(args_of(&cmd), vec!["changes", "-l"]);
864 }
865
866 #[test]
867 fn long_output_truncated() {
868 let changes = Changes::new("p4", GlobalOpts::default()).long_output_truncated();
869 let cmd = changes.setup_command("p4");
870 assert_eq!(args_of(&cmd), vec!["changes", "-L"]);
871 }
872
873 #[test]
874 fn limit() {
875 let changes = Changes::new("p4", GlobalOpts::default()).limit(5);
876 let cmd = changes.setup_command("p4");
877 assert_eq!(args_of(&cmd), vec!["changes", "-m", "5"]);
878 }
879
880 #[cfg(not(feature = "lt2017_2"))]
881 #[test]
882 fn reverse_order() {
883 let changes = Changes::new("p4", GlobalOpts::default()).reverse_order(true);
884 let cmd = changes.setup_command("p4");
885 assert_eq!(args_of(&cmd), vec!["changes", "-r"]);
886 }
887
888 #[test]
889 fn status_pending() {
890 let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Pending);
891 let cmd = changes.setup_command("p4");
892 assert_eq!(args_of(&cmd), vec!["changes", "-s", "pending"]);
893 }
894
895 #[test]
896 fn status_submitted() {
897 let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Submitted);
898 let cmd = changes.setup_command("p4");
899 assert_eq!(args_of(&cmd), vec!["changes", "-s", "submitted"]);
900 }
901
902 #[test]
903 fn status_shelved() {
904 let changes = Changes::new("p4", GlobalOpts::default()).status(Status::Shelved);
905 let cmd = changes.setup_command("p4");
906 assert_eq!(args_of(&cmd), vec!["changes", "-s", "shelved"]);
907 }
908
909 #[test]
910 fn include_time() {
911 let changes = Changes::new("p4", GlobalOpts::default()).include_time(true);
912 let cmd = changes.setup_command("p4");
913 assert_eq!(args_of(&cmd), vec!["changes", "-t"]);
914 }
915
916 #[test]
917 fn user_name() {
918 let changes = Changes::new("p4", GlobalOpts::default()).filter_user("edk");
919 let cmd = changes.setup_command("p4");
920 assert_eq!(args_of(&cmd), vec!["changes", "-u", "edk"]);
921 }
922
923 #[test]
924 fn multiple_users() {
925 let changes = Changes::new("p4", GlobalOpts::default())
926 .filter_user("maria")
927 .filter_user("edk");
928 let cmd = changes.setup_command("p4");
929 assert_eq!(args_of(&cmd), vec!["changes", "-u", "maria", "-u", "edk"]);
930 }
931
932 #[cfg(not(feature = "lt2016_1"))]
933 #[test]
934 fn user_me() {
935 let changes = Changes::new("p4", GlobalOpts::default()).filter_me();
936 let cmd = changes.setup_command("p4");
937 assert_eq!(args_of(&cmd), vec!["changes", "--me"]);
938 }
939
940 #[cfg(not(feature = "lt2016_1"))]
941 #[test]
942 fn multiple_users_with_me() {
943 let changes = Changes::new("p4", GlobalOpts::default())
944 .filter_user("maria")
945 .filter_me();
946 let cmd = changes.setup_command("p4");
947 assert_eq!(args_of(&cmd), vec!["changes", "-u", "maria", "--me"]);
948 }
949
950 #[cfg(not(feature = "lt2025_1"))]
951 #[test]
952 fn client_case_insensitive() {
953 let changes = Changes::new("p4", GlobalOpts::default())
954 .filter_client("eds_elm")
955 .client_case_insensitive(true);
956 let cmd = changes.setup_command("p4");
957 assert_eq!(
958 args_of(&cmd),
959 vec!["changes", "-c", "eds_elm", "--client-case-insensitive"]
960 );
961 }
962
963 #[cfg(not(feature = "lt2022_2"))]
964 #[test]
965 fn stream_spec() {
966 let changes = Changes::new("p4", GlobalOpts::default()).stream(true);
967 let cmd = changes.setup_command("p4");
968 assert_eq!(args_of(&cmd), vec!["changes", "--stream"]);
969 }
970
971 #[cfg(not(feature = "lt2022_2"))]
972 #[test]
973 fn no_stream_spec() {
974 let changes = Changes::new("p4", GlobalOpts::default()).stream(false);
975 let cmd = changes.setup_command("p4");
976 assert_eq!(args_of(&cmd), vec!["changes", "--nostream"]);
977 }
978
979 #[test]
980 fn all_options_order() {
981 let changes = Changes::new("p4", GlobalOpts::default())
982 .filter_client("eds_elm")
983 .include_restricted(true)
984 .include_integrated(true)
985 .long_output_full()
986 .limit(5)
987 .status(Status::Submitted)
988 .include_time(true)
989 .filter_user("edk");
990 #[cfg(not(feature = "lt2015_2"))]
991 let changes = changes.min_change_list("800");
992 #[cfg(not(feature = "lt2017_2"))]
993 let changes = changes.reverse_order(true);
994 #[cfg(not(feature = "lt2022_2"))]
995 let changes = changes.stream(true);
996 let cmd = changes.setup_command("p4");
997 let mut expected = vec!["changes", "-c", "eds_elm"];
998 #[cfg(not(feature = "lt2015_2"))]
999 expected.extend(["-e", "800"]);
1000 expected.extend(["-f", "-i", "-l", "-m", "5"]);
1001 #[cfg(not(feature = "lt2017_2"))]
1002 expected.push("-r");
1003 expected.extend(["-s", "submitted", "-t", "-u", "edk"]);
1004 #[cfg(not(feature = "lt2022_2"))]
1005 expected.push("--stream");
1006 assert_eq!(args_of(&cmd), expected);
1007 }
1008
1009 #[test]
1010 fn long_output_full_preserves_other_options() {
1011 let changes = Changes::new("p4", GlobalOpts::default())
1012 .include_restricted(true)
1013 .include_time(true)
1014 .long_output_full();
1015 let cmd = changes.setup_command("p4");
1016 assert_eq!(args_of(&cmd), vec!["changes", "-f", "-l", "-t"]);
1017 }
1018}