1use std::{
2 ffi::OsStr,
3 path::PathBuf,
4 process::{Child, Command, Stdio},
5};
6
7use super::{ExclusiveOption, SubCommand, Unselected};
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12#[derive(Debug, Clone, Default)]
18pub struct ParallelConfig {
19 pub threads: u64,
21
22 pub batch_files: Option<u64>,
24
25 pub batch_size_bytes: Option<u64>,
27
28 pub min_files: Option<u64>,
30
31 pub min_size_bytes: Option<u64>,
33}
34
35impl ParallelConfig {
36 pub fn as_arg(&self) -> String {
39 let mut parts = vec![format!("threads={}", self.threads)];
40
41 if let Some(v) = self.batch_files {
42 parts.push(format!("batch={}", v));
43 }
44 if let Some(v) = self.batch_size_bytes {
45 parts.push(format!("batchsize={}", v));
46 }
47 if let Some(v) = self.min_files {
48 parts.push(format!("min={}", v));
49 }
50 if let Some(v) = self.min_size_bytes {
51 parts.push(format!("minsize={}", v));
52 }
53
54 parts.join(",")
55 }
56}
57
58#[cfg(not(feature = "lt2022_2"))]
61#[derive(Debug, Clone, Copy)]
62pub enum StreamSpecVersion {
63 MaxInFilelists,
66 Current,
68 ChangeNumber(u32),
71}
72
73#[cfg(not(feature = "lt2022_2"))]
74impl StreamSpecVersion {
75 pub fn max_in_filelists() -> Self {
78 StreamSpecVersion::MaxInFilelists
79 }
80
81 pub fn current() -> Self {
83 StreamSpecVersion::Current
84 }
85
86 pub fn at_change(n: u32) -> Self {
89 StreamSpecVersion::ChangeNumber(n)
90 }
91
92 pub fn inject_arg(&self, command: &mut Command) {
94 match self {
95 StreamSpecVersion::MaxInFilelists => {
96 command.arg("--use-stream-change");
97 }
98 StreamSpecVersion::Current => {
99 command.arg("--use-stream-change=0");
100 }
101 StreamSpecVersion::ChangeNumber(n) => {
102 command.arg(format!("--use-stream-change={}", n));
103 }
104 }
105 }
106}
107
108#[derive(Debug, Clone, Copy, Default)]
114pub struct PreviewResult;
115
116impl ExclusiveOption for PreviewResult {
117 fn inject_args(&self, command: &mut Command) {
118 command.arg("-n");
119 }
120}
121
122#[derive(Debug, Clone, Copy, Default)]
128pub struct PreviewNetworkTraffic;
129
130impl ExclusiveOption for PreviewNetworkTraffic {
131 fn inject_args(&self, command: &mut Command) {
132 command.arg("-N");
133 }
134}
135
136#[derive(Debug, Clone, Default)]
142pub struct ForceRegularMode {
143 force: bool,
144
145 metadata_only: bool,
146
147 #[cfg(not(feature = "lt2015_1"))]
148 reopen_moved_files: bool,
149}
150
151impl ExclusiveOption for ForceRegularMode {
152 fn inject_args(&self, command: &mut Command) {
153 if self.force {
154 command.arg("-f");
155 }
156
157 if self.metadata_only {
158 command.arg("-k");
159 }
160
161 #[cfg(not(feature = "lt2015_1"))]
162 if self.reopen_moved_files {
163 command.arg("-r");
164 }
165 }
166}
167
168#[derive(Debug, Clone, Copy, Default)]
174pub struct SafeCheckMode;
175
176impl ExclusiveOption for SafeCheckMode {
177 fn inject_args(&self, command: &mut Command) {
178 command.arg("-s");
179 }
180}
181
182#[derive(Debug, Clone, Copy, Default)]
187pub struct PopulateMode;
188
189impl ExclusiveOption for PopulateMode {
190 fn inject_args(&self, command: &mut Command) {
191 command.arg("-p");
192 }
193}
194
195#[derive(Debug, Clone, Default)]
210pub struct RegularMode<Mode = Unselected, P = Unselected> {
211 #[cfg(not(feature = "lt2022_2"))]
212 verify_edge_replication: bool,
213
214 script_list_mode: bool,
215
216 #[cfg(not(feature = "lt2022_1"))]
217 suppress_keyword_expansion: bool,
218
219 quiet_mode: bool,
220
221 limit: Option<u64>,
222
223 parallel: Option<ParallelConfig>,
224
225 #[cfg(not(feature = "lt2022_2"))]
226 stream_spec_version: Option<StreamSpecVersion>,
227
228 mode: Mode,
229
230 preview: P,
231}
232
233impl<Mode: ExclusiveOption, P: ExclusiveOption> ExclusiveOption for RegularMode<Mode, P> {
234 fn inject_args(&self, command: &mut Command) {
235 #[cfg(not(feature = "lt2022_2"))]
236 if self.verify_edge_replication {
237 command.arg("-E");
238 }
239
240 if self.script_list_mode {
241 command.arg("-L");
242 }
243
244 #[cfg(not(feature = "lt2022_1"))]
245 if self.suppress_keyword_expansion {
246 command.arg("-K");
247 }
248
249 if self.quiet_mode {
250 command.arg("-q");
251 }
252
253 self.mode.inject_args(command);
254
255 self.preview.inject_args(command);
256
257 if let Some(max) = self.limit {
258 command.arg("-m").arg(max.to_string());
259 }
260
261 if let Some(parallel) = &self.parallel {
262 command.arg(format!("--parallel={}", parallel.as_arg()));
263 }
264
265 #[cfg(not(feature = "lt2022_2"))]
266 if let Some(version) = &self.stream_spec_version {
267 version.inject_arg(command);
268 }
269 }
270}
271
272#[cfg(not(feature = "lt2025_1"))]
279#[derive(Debug, Clone)]
280pub struct SyncTimeMode {
281 sync_time: String,
282}
283
284#[cfg(not(feature = "lt2025_1"))]
285impl ExclusiveOption for SyncTimeMode {
286 fn inject_args(&self, command: &mut Command) {
287 command
288 .arg("-k")
289 .arg(format!("--sync-time={}", self.sync_time));
290 }
291}
292
293#[derive(Debug, Clone, Default)]
301pub struct Sync<M = Unselected> {
302 bin: PathBuf,
303
304 global_opts: GlobalOpts,
305
306 mode: M,
307}
308
309impl Sync<Unselected> {
310 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
314 Self {
315 bin: bin.into(),
316 global_opts,
317 mode: Unselected,
318 }
319 }
320
321 #[cfg_attr(
328 all(feature = "lt2025_2", not(feature = "lt2025_1")),
329 doc = "The value of `N` can be Unix epoch time or the perforce date",
330 doc = "time format."
331 )]
332 #[cfg_attr(
333 not(feature = "lt2025_2"),
334 doc = "The value of `N` can be Unix epoch time or the Perforce date",
335 doc = "time format."
336 )]
337 #[cfg(not(feature = "lt2025_1"))]
341 pub fn sync_time(self, time: impl Into<String>) -> Sync<SyncTimeMode> {
342 Sync {
343 bin: self.bin,
344 global_opts: self.global_opts,
345 mode: SyncTimeMode {
346 sync_time: time.into(),
347 },
348 }
349 }
350
351 #[cfg_attr(
358 feature = "lt2017_2",
359 doc = "If the file was modified outside of Perforce control, an error",
360 doc = "message is displayed and the file is not overwritten."
361 )]
362 #[cfg_attr(
363 all(feature = "lt2024_1", not(feature = "lt2017_2")),
364 doc = "If the file was modified outside of the control of Helix",
365 doc = "Server, an error message is displayed and the file is not",
366 doc = "overwritten."
367 )]
368 #[cfg_attr(
369 all(feature = "lt2024_2", not(feature = "lt2024_1")),
370 doc = "If the file was modified outside of the control of Helix Core",
371 doc = "Server, an error message is displayed and the file is not",
372 doc = "overwritten."
373 )]
374 #[cfg_attr(
375 not(feature = "lt2024_2"),
376 doc = "If the file was modified outside of the control of P4 Server,",
377 doc = "an error message is displayed and the file is not overwritten."
378 )]
379 pub fn enable_safe_check(self) -> Sync<RegularMode<SafeCheckMode>> {
383 Sync {
384 bin: self.bin,
385 global_opts: self.global_opts,
386 mode: RegularMode {
387 mode: SafeCheckMode,
388 ..RegularMode::default()
389 },
390 }
391 }
392
393 pub fn populate_client_workspace(self) -> Sync<RegularMode<PopulateMode>> {
404 Sync {
405 bin: self.bin,
406 global_opts: self.global_opts,
407 mode: RegularMode {
408 mode: PopulateMode,
409 ..RegularMode::default()
410 },
411 }
412 }
413
414 #[cfg(not(feature = "lt2022_2"))]
424 pub fn verify_edge_replication(self, v: bool) -> Sync<RegularMode> {
425 Sync {
426 bin: self.bin,
427 global_opts: self.global_opts,
428 mode: RegularMode {
429 verify_edge_replication: v,
430 ..RegularMode::default()
431 },
432 }
433 }
434
435 #[cfg_attr(
442 not(feature = "lt2016_1"),
443 doc = "",
444 doc = "When this flag is used, the arguments are processed together by",
445 doc = "building an internal table similar to a label. This file list",
446 doc = "processing is significantly faster than having to call the",
447 doc = "internal query engine for each individual file argument. However,",
448 doc = "the file argument syntax is strict and the command will not run",
449 doc = "if an error is encountered."
450 )]
451 pub fn script_list_mode(self, v: bool) -> Sync<RegularMode> {
454 Sync {
455 bin: self.bin,
456 global_opts: self.global_opts,
457 mode: RegularMode {
458 script_list_mode: v,
459 ..RegularMode::default()
460 },
461 }
462 }
463
464 #[cfg_attr(feature = "lt2024_2", doc = "See File type modifiers.")]
471 #[cfg_attr(
472 not(feature = "lt2024_2"),
473 doc = "To learn more, see File type modifiers."
474 )]
475 #[cfg(not(feature = "lt2022_1"))]
478 pub fn suppress_keyword_expansion(self, v: bool) -> Sync<RegularMode> {
479 Sync {
480 bin: self.bin,
481 global_opts: self.global_opts,
482 mode: RegularMode {
483 suppress_keyword_expansion: v,
484 ..RegularMode::default()
485 },
486 }
487 }
488
489 pub fn quiet_mode(self, v: bool) -> Sync<RegularMode> {
498 Sync {
499 bin: self.bin,
500 global_opts: self.global_opts,
501 mode: RegularMode {
502 quiet_mode: v,
503 ..RegularMode::default()
504 },
505 }
506 }
507
508 #[cfg_attr(
514 not(feature = "lt2022_2"),
515 doc = "",
516 doc = "This option is useful in conjunction with tagged output and the",
517 doc = "`-n` flag, to preview how many files will be synced without",
518 doc = "transferring all the file data."
519 )]
520 pub fn limit(self, v: u64) -> Sync<RegularMode> {
523 Sync {
524 bin: self.bin,
525 global_opts: self.global_opts,
526 mode: RegularMode {
527 limit: Some(v),
528 ..RegularMode::default()
529 },
530 }
531 }
532
533 pub fn parallel(self, v: ParallelConfig) -> Sync<RegularMode> {
541 Sync {
542 bin: self.bin,
543 global_opts: self.global_opts,
544 mode: RegularMode {
545 parallel: Some(v),
546 ..RegularMode::default()
547 },
548 }
549 }
550
551 #[cfg(not(feature = "lt2022_2"))]
560 pub fn stream_spec_version(self, v: StreamSpecVersion) -> Sync<RegularMode> {
561 Sync {
562 bin: self.bin,
563 global_opts: self.global_opts,
564 mode: RegularMode {
565 stream_spec_version: Some(v),
566 ..RegularMode::default()
567 },
568 }
569 }
570
571 #[cfg(not(feature = "lt2022_2"))]
576 pub fn sc_max_change_number(self) -> Sync<RegularMode> {
577 self.stream_spec_version(StreamSpecVersion::MaxInFilelists)
578 }
579
580 #[cfg(not(feature = "lt2022_2"))]
584 pub fn sc_current_stream_spec(self) -> Sync<RegularMode> {
585 self.stream_spec_version(StreamSpecVersion::Current)
586 }
587
588 #[cfg(not(feature = "lt2022_2"))]
593 pub fn sc_change_number(self, n: u32) -> Sync<RegularMode> {
594 self.stream_spec_version(StreamSpecVersion::ChangeNumber(n))
595 }
596
597 #[cfg_attr(
602 feature = "lt2016_1",
603 doc = "Display the results of the sync without actually performing the",
604 doc = "sync.",
605 doc = "",
606 doc = "This lets you make sure that the sync does what you think it",
607 doc = "does before you do it."
608 )]
609 #[cfg_attr(
610 not(feature = "lt2016_1"),
611 doc = "Preview mode: display the results of the sync without actually",
612 doc = "performing the sync."
613 )]
614 pub fn preview_result(self) -> Sync<RegularMode<Unselected, PreviewResult>> {
618 Sync {
619 bin: self.bin,
620 global_opts: self.global_opts,
621 mode: RegularMode {
622 preview: PreviewResult,
623 ..RegularMode::default()
624 },
625 }
626 }
627
628 #[cfg_attr(
633 feature = "lt2016_1",
634 doc = "Display a summary of the expected network traffic associated",
635 doc = "with a sync, without performing the sync."
636 )]
637 #[cfg_attr(
638 all(feature = "lt2021_2", not(feature = "lt2016_1")),
639 doc = "Preview mode: display a summary of the expected network traffic",
640 doc = "associated with a sync, without performing the sync."
641 )]
642 #[cfg_attr(
643 not(feature = "lt2021_2"),
644 doc = "Preview mode: display a summary of the expected network traffic",
645 doc = "associated with a sync, without performing the sync.",
646 doc = "",
647 doc = "This tells you how many files are to be added or updated, which",
648 doc = "is useful if there are many large files, limits on bandwidth, or",
649 doc = "limits on disk space."
650 )]
651 pub fn preview_network_traffic(self) -> Sync<RegularMode<Unselected, PreviewNetworkTraffic>> {
655 Sync {
656 bin: self.bin,
657 global_opts: self.global_opts,
658 mode: RegularMode {
659 preview: PreviewNetworkTraffic,
660 ..RegularMode::default()
661 },
662 }
663 }
664
665 #[cfg_attr(
670 feature = "lt2014_2",
671 doc = "Force the sync. Perforce performs the sync even if the client",
672 doc = "workspace already has the file at the specified revision. If the",
673 doc = "file is writable, it is overwritten.",
674 doc = "",
675 doc = "This flag does not affect open files, but it does override the",
676 doc = "noclobber client option."
677 )]
678 #[cfg_attr(
679 all(feature = "lt2017_2", not(feature = "lt2014_2")),
680 doc = "Force the sync. Perforce performs the sync even if the client",
681 doc = "workspace already has the file at the specified revision. If the",
682 doc = "file is writable, it is overwritten.",
683 doc = "",
684 doc = "This option does not affect open files, but it does override the",
685 doc = "noclobber client option."
686 )]
687 #[cfg_attr(
688 all(feature = "lt2024_1", not(feature = "lt2017_2")),
689 doc = "Force the sync. Helix Server performs the sync even if the",
690 doc = "client workspace already has the file at the specified",
691 doc = "revision. If the file is writable, it is overwritten.",
692 doc = "",
693 doc = "This option does not affect open files, but it does override the",
694 doc = "noclobber client option (see p4 client)."
695 )]
696 #[cfg_attr(
697 all(feature = "lt2024_2", not(feature = "lt2024_1")),
698 doc = "Force the sync. Helix Core Server performs the sync even if the",
699 doc = "client workspace already has the file at the specified",
700 doc = "revision. If the file is writable, it is overwritten.",
701 doc = "",
702 doc = "This option does not affect open files, but it does override the",
703 doc = "noclobber client option (see p4 client)."
704 )]
705 #[cfg_attr(
706 not(feature = "lt2024_2"),
707 doc = "Force the sync. P4 Server performs the sync even if the client",
708 doc = "workspace already has the file at the specified revision. If the",
709 doc = "file is writable, it is overwritten.",
710 doc = "",
711 doc = "This option does not affect open files, but it does override the",
712 doc = "noclobber client option (see p4 client)."
713 )]
714 pub fn force(self, v: bool) -> Sync<RegularMode<ForceRegularMode>> {
719 Sync {
720 bin: self.bin,
721 global_opts: self.global_opts,
722 mode: RegularMode {
723 mode: ForceRegularMode {
724 force: v,
725 ..ForceRegularMode::default()
726 },
727 ..RegularMode::default()
728 },
729 }
730 }
731
732 #[cfg_attr(
737 feature = "lt2022_2",
738 doc = "Keep existing workspace files; update the have list without",
739 doc = "updating the client workspace."
740 )]
741 #[cfg_attr(
742 not(feature = "lt2022_2"),
743 doc = "Update server metadata without syncing files. Keep existing",
744 doc = "workspace files and update the have list without updating the",
745 doc = "client workspace."
746 )]
747 pub fn metadata_only(self, v: bool) -> Sync<RegularMode<ForceRegularMode>> {
752 Sync {
753 bin: self.bin,
754 global_opts: self.global_opts,
755 mode: RegularMode {
756 mode: ForceRegularMode {
757 metadata_only: v,
758 ..ForceRegularMode::default()
759 },
760 ..RegularMode::default()
761 },
762 }
763 }
764
765 #[cfg(not(feature = "lt2015_1"))]
776 pub fn reopen_moved_files(self, v: bool) -> Sync<RegularMode<ForceRegularMode>> {
777 Sync {
778 bin: self.bin,
779 global_opts: self.global_opts,
780 mode: RegularMode {
781 mode: ForceRegularMode {
782 reopen_moved_files: v,
783 ..ForceRegularMode::default()
784 },
785 ..RegularMode::default()
786 },
787 }
788 }
789}
790
791impl<Mode: ExclusiveOption, P: ExclusiveOption> Sync<RegularMode<Mode, P>> {
794 #[cfg(not(feature = "lt2022_2"))]
796 pub fn get_verify_edge_replication(&self) -> bool {
797 self.mode.verify_edge_replication
798 }
799
800 #[cfg(not(feature = "lt2022_2"))]
802 pub fn set_verify_edge_replication(&mut self, v: bool) -> &mut Self {
803 self.mode.verify_edge_replication = v;
804 self
805 }
806
807 #[cfg(not(feature = "lt2022_2"))]
809 pub fn verify_edge_replication(mut self, v: bool) -> Self {
810 self.mode.verify_edge_replication = v;
811 self
812 }
813
814 pub fn get_script_list_mode(&self) -> bool {
816 self.mode.script_list_mode
817 }
818
819 pub fn set_script_list_mode(&mut self, v: bool) -> &mut Self {
821 self.mode.script_list_mode = v;
822 self
823 }
824
825 pub fn script_list_mode(mut self, v: bool) -> Self {
827 self.mode.script_list_mode = v;
828 self
829 }
830
831 #[cfg(not(feature = "lt2022_1"))]
833 pub fn get_suppress_keyword_expansion(&self) -> bool {
834 self.mode.suppress_keyword_expansion
835 }
836
837 #[cfg(not(feature = "lt2022_1"))]
839 pub fn set_suppress_keyword_expansion(&mut self, v: bool) -> &mut Self {
840 self.mode.suppress_keyword_expansion = v;
841 self
842 }
843
844 #[cfg(not(feature = "lt2022_1"))]
846 pub fn suppress_keyword_expansion(mut self, v: bool) -> Self {
847 self.mode.suppress_keyword_expansion = v;
848 self
849 }
850
851 pub fn get_quiet_mode(&self) -> bool {
853 self.mode.quiet_mode
854 }
855
856 pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
858 self.mode.quiet_mode = v;
859 self
860 }
861
862 pub fn quiet_mode(mut self, v: bool) -> Self {
864 self.mode.quiet_mode = v;
865 self
866 }
867
868 pub fn get_limit(&self) -> Option<u64> {
870 self.mode.limit
871 }
872
873 pub fn set_limit(&mut self, v: u64) -> &mut Self {
875 self.mode.limit = Some(v);
876 self
877 }
878
879 pub fn limit(mut self, v: u64) -> Self {
881 self.mode.limit = Some(v);
882 self
883 }
884
885 pub fn get_parallel(&self) -> Option<&ParallelConfig> {
887 self.mode.parallel.as_ref()
888 }
889
890 pub fn set_parallel(&mut self, v: ParallelConfig) -> &mut Self {
892 self.mode.parallel = Some(v);
893 self
894 }
895
896 pub fn parallel(mut self, v: ParallelConfig) -> Self {
898 self.mode.parallel = Some(v);
899 self
900 }
901
902 #[cfg(not(feature = "lt2022_2"))]
904 pub fn get_stream_spec_version(&self) -> Option<StreamSpecVersion> {
905 self.mode.stream_spec_version
906 }
907
908 #[cfg(not(feature = "lt2022_2"))]
910 pub fn set_stream_spec_version(&mut self, v: StreamSpecVersion) -> &mut Self {
911 self.mode.stream_spec_version = Some(v);
912 self
913 }
914
915 #[cfg(not(feature = "lt2022_2"))]
917 pub fn stream_spec_version(mut self, v: StreamSpecVersion) -> Self {
918 self.mode.stream_spec_version = Some(v);
919 self
920 }
921
922 #[cfg(not(feature = "lt2022_2"))]
925 pub fn set_sc_max_change_number(&mut self) -> &mut Self {
926 self.mode.stream_spec_version = Some(StreamSpecVersion::MaxInFilelists);
927 self
928 }
929
930 #[cfg(not(feature = "lt2022_2"))]
933 pub fn sc_max_change_number(mut self) -> Self {
934 self.mode.stream_spec_version = Some(StreamSpecVersion::MaxInFilelists);
935 self
936 }
937
938 #[cfg(not(feature = "lt2022_2"))]
940 pub fn set_sc_current_stream_spec(&mut self) -> &mut Self {
941 self.mode.stream_spec_version = Some(StreamSpecVersion::Current);
942 self
943 }
944
945 #[cfg(not(feature = "lt2022_2"))]
947 pub fn sc_current_stream_spec(mut self) -> Self {
948 self.mode.stream_spec_version = Some(StreamSpecVersion::Current);
949 self
950 }
951
952 #[cfg(not(feature = "lt2022_2"))]
955 pub fn set_sc_change_number(&mut self, n: u32) -> &mut Self {
956 self.mode.stream_spec_version = Some(StreamSpecVersion::ChangeNumber(n));
957 self
958 }
959
960 #[cfg(not(feature = "lt2022_2"))]
963 pub fn sc_change_number(mut self, n: u32) -> Self {
964 self.mode.stream_spec_version = Some(StreamSpecVersion::ChangeNumber(n));
965 self
966 }
967}
968
969impl<P: ExclusiveOption> Sync<RegularMode<Unselected, P>> {
972 #[cfg_attr(
977 feature = "lt2014_2",
978 doc = "Force the sync. Perforce performs the sync even if the client",
979 doc = "workspace already has the file at the specified revision. If the",
980 doc = "file is writable, it is overwritten.",
981 doc = "",
982 doc = "This flag does not affect open files, but it does override the",
983 doc = "noclobber client option."
984 )]
985 #[cfg_attr(
986 all(feature = "lt2017_2", not(feature = "lt2014_2")),
987 doc = "Force the sync. Perforce performs the sync even if the client",
988 doc = "workspace already has the file at the specified revision. If the",
989 doc = "file is writable, it is overwritten.",
990 doc = "",
991 doc = "This option does not affect open files, but it does override the",
992 doc = "noclobber client option."
993 )]
994 #[cfg_attr(
995 all(feature = "lt2024_1", not(feature = "lt2017_2")),
996 doc = "Force the sync. Helix Server performs the sync even if the",
997 doc = "client workspace already has the file at the specified",
998 doc = "revision. If the file is writable, it is overwritten.",
999 doc = "",
1000 doc = "This option does not affect open files, but it does override the",
1001 doc = "noclobber client option (see p4 client)."
1002 )]
1003 #[cfg_attr(
1004 all(feature = "lt2024_2", not(feature = "lt2024_1")),
1005 doc = "Force the sync. Helix Core Server performs the sync even if the",
1006 doc = "client workspace already has the file at the specified",
1007 doc = "revision. If the file is writable, it is overwritten.",
1008 doc = "",
1009 doc = "This option does not affect open files, but it does override the",
1010 doc = "noclobber client option (see p4 client)."
1011 )]
1012 #[cfg_attr(
1013 not(feature = "lt2024_2"),
1014 doc = "Force the sync. P4 Server performs the sync even if the client",
1015 doc = "workspace already has the file at the specified revision. If the",
1016 doc = "file is writable, it is overwritten.",
1017 doc = "",
1018 doc = "This option does not affect open files, but it does override the",
1019 doc = "noclobber client option (see p4 client)."
1020 )]
1021 pub fn force(self, v: bool) -> Sync<RegularMode<ForceRegularMode, P>> {
1025 Sync {
1026 bin: self.bin,
1027 global_opts: self.global_opts,
1028 mode: RegularMode {
1029 #[cfg(not(feature = "lt2022_2"))]
1030 verify_edge_replication: self.mode.verify_edge_replication,
1031 script_list_mode: self.mode.script_list_mode,
1032 #[cfg(not(feature = "lt2022_1"))]
1033 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1034 quiet_mode: self.mode.quiet_mode,
1035 limit: self.mode.limit,
1036 parallel: self.mode.parallel,
1037 #[cfg(not(feature = "lt2022_2"))]
1038 stream_spec_version: self.mode.stream_spec_version,
1039 mode: ForceRegularMode {
1040 force: v,
1041 ..ForceRegularMode::default()
1042 },
1043 preview: self.mode.preview,
1044 },
1045 }
1046 }
1047
1048 #[cfg_attr(
1053 feature = "lt2022_2",
1054 doc = "Keep existing workspace files; update the have list without",
1055 doc = "updating the client workspace."
1056 )]
1057 #[cfg_attr(
1058 not(feature = "lt2022_2"),
1059 doc = "Update server metadata without syncing files. Keep existing",
1060 doc = "workspace files and update the have list without updating the",
1061 doc = "client workspace."
1062 )]
1063 pub fn metadata_only(self, v: bool) -> Sync<RegularMode<ForceRegularMode, P>> {
1067 Sync {
1068 bin: self.bin,
1069 global_opts: self.global_opts,
1070 mode: RegularMode {
1071 #[cfg(not(feature = "lt2022_2"))]
1072 verify_edge_replication: self.mode.verify_edge_replication,
1073 script_list_mode: self.mode.script_list_mode,
1074 #[cfg(not(feature = "lt2022_1"))]
1075 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1076 quiet_mode: self.mode.quiet_mode,
1077 limit: self.mode.limit,
1078 parallel: self.mode.parallel,
1079 #[cfg(not(feature = "lt2022_2"))]
1080 stream_spec_version: self.mode.stream_spec_version,
1081 mode: ForceRegularMode {
1082 metadata_only: v,
1083 ..ForceRegularMode::default()
1084 },
1085 preview: self.mode.preview,
1086 },
1087 }
1088 }
1089
1090 #[cfg(not(feature = "lt2015_1"))]
1100 pub fn reopen_moved_files(self, v: bool) -> Sync<RegularMode<ForceRegularMode, P>> {
1101 Sync {
1102 bin: self.bin,
1103 global_opts: self.global_opts,
1104 mode: RegularMode {
1105 #[cfg(not(feature = "lt2022_2"))]
1106 verify_edge_replication: self.mode.verify_edge_replication,
1107 script_list_mode: self.mode.script_list_mode,
1108 #[cfg(not(feature = "lt2022_1"))]
1109 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1110 quiet_mode: self.mode.quiet_mode,
1111 limit: self.mode.limit,
1112 parallel: self.mode.parallel,
1113 #[cfg(not(feature = "lt2022_2"))]
1114 stream_spec_version: self.mode.stream_spec_version,
1115 mode: ForceRegularMode {
1116 reopen_moved_files: v,
1117 ..ForceRegularMode::default()
1118 },
1119 preview: self.mode.preview,
1120 },
1121 }
1122 }
1123
1124 #[cfg_attr(
1131 feature = "lt2017_2",
1132 doc = "If the file was modified outside of Perforce control, an error",
1133 doc = "message is displayed and the file is not overwritten."
1134 )]
1135 #[cfg_attr(
1136 all(feature = "lt2024_1", not(feature = "lt2017_2")),
1137 doc = "If the file was modified outside of the control of Helix",
1138 doc = "Server, an error message is displayed and the file is not",
1139 doc = "overwritten."
1140 )]
1141 #[cfg_attr(
1142 all(feature = "lt2024_2", not(feature = "lt2024_1")),
1143 doc = "If the file was modified outside of the control of Helix Core",
1144 doc = "Server, an error message is displayed and the file is not",
1145 doc = "overwritten."
1146 )]
1147 #[cfg_attr(
1148 not(feature = "lt2024_2"),
1149 doc = "If the file was modified outside of the control of P4 Server,",
1150 doc = "an error message is displayed and the file is not overwritten."
1151 )]
1152 pub fn safe_check(self) -> Sync<RegularMode<SafeCheckMode, P>> {
1155 Sync {
1156 bin: self.bin,
1157 global_opts: self.global_opts,
1158 mode: RegularMode {
1159 #[cfg(not(feature = "lt2022_2"))]
1160 verify_edge_replication: self.mode.verify_edge_replication,
1161 script_list_mode: self.mode.script_list_mode,
1162 #[cfg(not(feature = "lt2022_1"))]
1163 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1164 quiet_mode: self.mode.quiet_mode,
1165 limit: self.mode.limit,
1166 parallel: self.mode.parallel,
1167 #[cfg(not(feature = "lt2022_2"))]
1168 stream_spec_version: self.mode.stream_spec_version,
1169 mode: SafeCheckMode,
1170 preview: self.mode.preview,
1171 },
1172 }
1173 }
1174
1175 pub fn populate(self) -> Sync<RegularMode<PopulateMode, P>> {
1185 Sync {
1186 bin: self.bin,
1187 global_opts: self.global_opts,
1188 mode: RegularMode {
1189 #[cfg(not(feature = "lt2022_2"))]
1190 verify_edge_replication: self.mode.verify_edge_replication,
1191 script_list_mode: self.mode.script_list_mode,
1192 #[cfg(not(feature = "lt2022_1"))]
1193 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1194 quiet_mode: self.mode.quiet_mode,
1195 limit: self.mode.limit,
1196 parallel: self.mode.parallel,
1197 #[cfg(not(feature = "lt2022_2"))]
1198 stream_spec_version: self.mode.stream_spec_version,
1199 mode: PopulateMode,
1200 preview: self.mode.preview,
1201 },
1202 }
1203 }
1204}
1205
1206impl<Mode: ExclusiveOption> Sync<RegularMode<Mode, Unselected>> {
1209 #[cfg_attr(
1214 feature = "lt2016_1",
1215 doc = "Display the results of the sync without actually performing the",
1216 doc = "sync.",
1217 doc = "",
1218 doc = "This lets you make sure that the sync does what you think it",
1219 doc = "does before you do it."
1220 )]
1221 #[cfg_attr(
1222 not(feature = "lt2016_1"),
1223 doc = "Preview mode: display the results of the sync without actually",
1224 doc = "performing the sync."
1225 )]
1226 pub fn preview_result(self) -> Sync<RegularMode<Mode, PreviewResult>> {
1229 Sync {
1230 bin: self.bin,
1231 global_opts: self.global_opts,
1232 mode: RegularMode {
1233 #[cfg(not(feature = "lt2022_2"))]
1234 verify_edge_replication: self.mode.verify_edge_replication,
1235 script_list_mode: self.mode.script_list_mode,
1236 #[cfg(not(feature = "lt2022_1"))]
1237 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1238 quiet_mode: self.mode.quiet_mode,
1239 limit: self.mode.limit,
1240 parallel: self.mode.parallel,
1241 #[cfg(not(feature = "lt2022_2"))]
1242 stream_spec_version: self.mode.stream_spec_version,
1243 mode: self.mode.mode,
1244 preview: PreviewResult,
1245 },
1246 }
1247 }
1248
1249 #[cfg_attr(
1254 feature = "lt2016_1",
1255 doc = "Display a summary of the expected network traffic associated",
1256 doc = "with a sync, without performing the sync."
1257 )]
1258 #[cfg_attr(
1259 all(feature = "lt2021_2", not(feature = "lt2016_1")),
1260 doc = "Preview mode: display a summary of the expected network traffic",
1261 doc = "associated with a sync, without performing the sync."
1262 )]
1263 #[cfg_attr(
1264 not(feature = "lt2021_2"),
1265 doc = "Preview mode: display a summary of the expected network traffic",
1266 doc = "associated with a sync, without performing the sync.",
1267 doc = "",
1268 doc = "This tells you how many files are to be added or updated, which",
1269 doc = "is useful if there are many large files, limits on bandwidth, or",
1270 doc = "limits on disk space."
1271 )]
1272 pub fn preview_network_traffic(self) -> Sync<RegularMode<Mode, PreviewNetworkTraffic>> {
1275 Sync {
1276 bin: self.bin,
1277 global_opts: self.global_opts,
1278 mode: RegularMode {
1279 #[cfg(not(feature = "lt2022_2"))]
1280 verify_edge_replication: self.mode.verify_edge_replication,
1281 script_list_mode: self.mode.script_list_mode,
1282 #[cfg(not(feature = "lt2022_1"))]
1283 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1284 quiet_mode: self.mode.quiet_mode,
1285 limit: self.mode.limit,
1286 parallel: self.mode.parallel,
1287 #[cfg(not(feature = "lt2022_2"))]
1288 stream_spec_version: self.mode.stream_spec_version,
1289 mode: self.mode.mode,
1290 preview: PreviewNetworkTraffic,
1291 },
1292 }
1293 }
1294}
1295
1296impl<P: ExclusiveOption> Sync<RegularMode<ForceRegularMode, P>> {
1299 pub fn get_force(&self) -> bool {
1301 self.mode.mode.force
1302 }
1303
1304 pub fn set_force(&mut self, v: bool) -> &mut Self {
1306 self.mode.mode.force = v;
1307 self
1308 }
1309
1310 pub fn force(mut self, v: bool) -> Self {
1312 self.mode.mode.force = v;
1313 self
1314 }
1315
1316 pub fn get_metadata_only(&self) -> bool {
1318 self.mode.mode.metadata_only
1319 }
1320
1321 pub fn set_metadata_only(&mut self, v: bool) -> &mut Self {
1323 self.mode.mode.metadata_only = v;
1324 self
1325 }
1326
1327 pub fn metadata_only(mut self, v: bool) -> Self {
1329 self.mode.mode.metadata_only = v;
1330 self
1331 }
1332
1333 #[cfg(not(feature = "lt2015_1"))]
1335 pub fn get_reopen_moved_files(&self) -> bool {
1336 self.mode.mode.reopen_moved_files
1337 }
1338
1339 #[cfg(not(feature = "lt2015_1"))]
1341 pub fn set_reopen_moved_files(&mut self, v: bool) -> &mut Self {
1342 self.mode.mode.reopen_moved_files = v;
1343 self
1344 }
1345
1346 #[cfg(not(feature = "lt2015_1"))]
1348 pub fn reopen_moved_files(mut self, v: bool) -> Self {
1349 self.mode.mode.reopen_moved_files = v;
1350 self
1351 }
1352}
1353
1354#[cfg(not(feature = "lt2025_1"))]
1357impl Sync<SyncTimeMode> {
1358 pub fn get_sync_time(&self) -> &str {
1360 &self.mode.sync_time
1361 }
1362
1363 pub fn set_sync_time(&mut self, v: impl Into<String>) -> &mut Self {
1366 self.mode.sync_time = v.into();
1367 self
1368 }
1369
1370 pub fn sync_time(mut self, v: impl Into<String>) -> Self {
1373 self.mode.sync_time = v.into();
1374 self
1375 }
1376}
1377
1378impl<M: ExclusiveOption, S, I> ParameterizedSpawn<(S,)> for Sync<M>
1381where
1382 S: IntoIterator<Item = I>,
1383 I: AsRef<OsStr>,
1384{
1385 type Output = Child;
1386 type Error = std::io::Error;
1387
1388 fn spawn_with(&mut self, (files,): (S,)) -> Result<Self::Output, Self::Error> {
1392 self.setup_command(&self.bin)
1393 .args(files)
1394 .stdout(Stdio::piped())
1395 .stderr(Stdio::piped())
1396 .spawn()
1397 }
1398}
1399
1400impl<M: ExclusiveOption> Sync<M> {
1401 #[cfg_attr(
1406 feature = "lt2014_2",
1407 doc = "See the [Global Options](GlobalOpts) section."
1408 )]
1409 #[cfg_attr(
1410 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1411 doc = "See the [“Global Options”](GlobalOpts) section."
1412 )]
1413 #[cfg_attr(
1414 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1415 doc = "See [“Global Options”](GlobalOpts)."
1416 )]
1417 #[cfg_attr(
1418 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1419 doc = "See [Global Options](GlobalOpts)."
1420 )]
1421 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1422 pub fn get_global_opts(&self) -> &GlobalOpts {
1423 &self.global_opts
1424 }
1425
1426 #[cfg_attr(
1431 feature = "lt2014_2",
1432 doc = "See the [Global Options](GlobalOpts) section."
1433 )]
1434 #[cfg_attr(
1435 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1436 doc = "See the [“Global Options”](GlobalOpts) section."
1437 )]
1438 #[cfg_attr(
1439 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1440 doc = "See [“Global Options”](GlobalOpts)."
1441 )]
1442 #[cfg_attr(
1443 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1444 doc = "See [Global Options](GlobalOpts)."
1445 )]
1446 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1447 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
1448 self.global_opts = v;
1449 self
1450 }
1451
1452 #[cfg_attr(
1457 feature = "lt2014_2",
1458 doc = "See the [Global Options](GlobalOpts) section."
1459 )]
1460 #[cfg_attr(
1461 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1462 doc = "See the [“Global Options”](GlobalOpts) section."
1463 )]
1464 #[cfg_attr(
1465 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1466 doc = "See [“Global Options”](GlobalOpts)."
1467 )]
1468 #[cfg_attr(
1469 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1470 doc = "See [Global Options](GlobalOpts)."
1471 )]
1472 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1473 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
1474 self.global_opts = v;
1475 self
1476 }
1477}
1478
1479impl<M: ExclusiveOption> SubCommand for Sync<M> {
1480 fn name(&self) -> &str {
1481 "sync"
1482 }
1483
1484 fn inject_local_args(&self, command: &mut Command) {
1485 self.mode.inject_args(command);
1486 }
1487
1488 fn global_opts(&self) -> Option<&GlobalOpts> {
1489 Some(&self.global_opts)
1490 }
1491}
1492
1493#[cfg(test)]
1494mod tests {
1495 use super::*;
1496 use crate::cmd::args_of;
1497
1498 #[test]
1499 fn without_options() {
1500 let sync = Sync::new("p4", GlobalOpts::new());
1501
1502 assert_eq!(args_of(&sync.setup_command("p4")), ["sync"]);
1503 }
1504
1505 #[test]
1506 #[cfg(not(feature = "lt2025_1"))]
1507 fn sync_time_mode() {
1508 let sync = Sync::new("p4", GlobalOpts::new()).sync_time("2024/01/01");
1509
1510 assert_eq!(
1511 args_of(&sync.setup_command("p4")),
1512 ["sync", "-k", "--sync-time=2024/01/01"]
1513 );
1514 }
1515
1516 #[test]
1517 #[cfg(not(feature = "lt2025_1"))]
1518 fn sync_time_mode_epoch() {
1519 let sync = Sync::new("p4", GlobalOpts::new()).sync_time("1700000000");
1520
1521 assert_eq!(
1522 args_of(&sync.setup_command("p4")),
1523 ["sync", "-k", "--sync-time=1700000000"]
1524 );
1525 }
1526
1527 #[test]
1528 #[cfg(not(feature = "lt2025_1"))]
1529 fn sync_time_set_style() {
1530 let mut sync = Sync::new("p4", GlobalOpts::new()).sync_time("2024/01/01");
1531 sync.set_sync_time("2024/06/01");
1532
1533 assert_eq!(sync.get_sync_time(), "2024/06/01");
1534 assert_eq!(
1535 args_of(&sync.setup_command("p4")),
1536 ["sync", "-k", "--sync-time=2024/06/01"]
1537 );
1538 }
1539
1540 #[test]
1541 fn regular_mode_common_options() {
1542 let sync = Sync::new("p4", GlobalOpts::new())
1543 .script_list_mode(true)
1544 .quiet_mode(true)
1545 .limit(5);
1546
1547 #[cfg(not(feature = "lt2022_2"))]
1548 {
1549 let sync = sync.verify_edge_replication(true);
1550 assert_eq!(
1551 args_of(&sync.setup_command("p4")),
1552 ["sync", "-E", "-L", "-q", "-m", "5"]
1553 );
1554 }
1555
1556 #[cfg(feature = "lt2022_2")]
1557 assert_eq!(
1558 args_of(&sync.setup_command("p4")),
1559 ["sync", "-L", "-q", "-m", "5"]
1560 );
1561 }
1562
1563 #[test]
1564 fn regular_mode_force_options() {
1565 let sync = Sync::new("p4", GlobalOpts::new())
1566 .force(true)
1567 .metadata_only(true);
1568
1569 #[cfg(not(feature = "lt2015_1"))]
1570 let sync = sync.reopen_moved_files(true);
1571
1572 #[cfg(not(feature = "lt2015_1"))]
1573 let expected = vec!["sync", "-f", "-k", "-r"];
1574 #[cfg(feature = "lt2015_1")]
1575 let expected = vec!["sync", "-f", "-k"];
1576
1577 assert_eq!(args_of(&sync.setup_command("p4")), expected);
1578 }
1579
1580 #[test]
1581 fn regular_mode_combined() {
1582 let sync = Sync::new("p4", GlobalOpts::new())
1583 .quiet_mode(true)
1584 .force(true)
1585 .limit(10);
1586
1587 assert_eq!(
1588 args_of(&sync.setup_command("p4")),
1589 ["sync", "-q", "-f", "-m", "10"]
1590 );
1591 }
1592
1593 #[test]
1594 fn regular_mode_preview_result() {
1595 let sync = Sync::new("p4", GlobalOpts::new()).preview_result();
1596
1597 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-n"]);
1598 }
1599
1600 #[test]
1601 fn regular_mode_preview_network_traffic() {
1602 let sync = Sync::new("p4", GlobalOpts::new()).preview_network_traffic();
1603
1604 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-N"]);
1605 }
1606
1607 #[test]
1608 fn regular_mode_preview_with_options() {
1609 let sync = Sync::new("p4", GlobalOpts::new())
1610 .quiet_mode(true)
1611 .preview_result()
1612 .limit(3);
1613
1614 assert_eq!(
1615 args_of(&sync.setup_command("p4")),
1616 ["sync", "-q", "-n", "-m", "3"]
1617 );
1618 }
1619
1620 #[test]
1621 fn regular_mode_parallel() {
1622 let sync = Sync::new("p4", GlobalOpts::new()).parallel(ParallelConfig {
1623 threads: 4,
1624 batch_files: Some(8),
1625 batch_size_bytes: None,
1626 min_files: Some(9),
1627 min_size_bytes: None,
1628 });
1629
1630 assert_eq!(
1631 args_of(&sync.setup_command("p4")),
1632 ["sync", "--parallel=threads=4,batch=8,min=9"]
1633 );
1634 }
1635
1636 #[test]
1637 #[cfg(not(feature = "lt2022_2"))]
1638 fn regular_mode_stream_spec_auto() {
1639 let sync = Sync::new("p4", GlobalOpts::new())
1640 .stream_spec_version(StreamSpecVersion::MaxInFilelists);
1641
1642 assert_eq!(
1643 args_of(&sync.setup_command("p4")),
1644 ["sync", "--use-stream-change"]
1645 );
1646 }
1647
1648 #[test]
1649 #[cfg(not(feature = "lt2022_2"))]
1650 fn regular_mode_stream_spec_current() {
1651 let sync =
1652 Sync::new("p4", GlobalOpts::new()).stream_spec_version(StreamSpecVersion::Current);
1653
1654 assert_eq!(
1655 args_of(&sync.setup_command("p4")),
1656 ["sync", "--use-stream-change=0"]
1657 );
1658 }
1659
1660 #[test]
1661 #[cfg(not(feature = "lt2022_2"))]
1662 fn regular_mode_stream_spec_specific() {
1663 let sync = Sync::new("p4", GlobalOpts::new())
1664 .stream_spec_version(StreamSpecVersion::ChangeNumber(123));
1665
1666 assert_eq!(
1667 args_of(&sync.setup_command("p4")),
1668 ["sync", "--use-stream-change=123"]
1669 );
1670 }
1671
1672 #[test]
1673 fn safe_check_mode() {
1674 let sync = Sync::new("p4", GlobalOpts::new()).enable_safe_check();
1675
1676 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-s"]);
1677 }
1678
1679 #[test]
1680 fn safe_check_mode_with_options() {
1681 let sync = Sync::new("p4", GlobalOpts::new())
1682 .enable_safe_check()
1683 .quiet_mode(true)
1684 .limit(5);
1685
1686 assert_eq!(
1687 args_of(&sync.setup_command("p4")),
1688 ["sync", "-q", "-s", "-m", "5"]
1689 );
1690 }
1691
1692 #[test]
1693 fn populate_mode() {
1694 let sync = Sync::new("p4", GlobalOpts::new()).populate_client_workspace();
1695
1696 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-p"]);
1697 }
1698
1699 #[test]
1700 fn populate_mode_with_options() {
1701 let sync = Sync::new("p4", GlobalOpts::new())
1702 .populate_client_workspace()
1703 .quiet_mode(true)
1704 .limit(5);
1705
1706 assert_eq!(
1707 args_of(&sync.setup_command("p4")),
1708 ["sync", "-q", "-p", "-m", "5"]
1709 );
1710 }
1711
1712 #[test]
1713 fn transition_to_safe_check_from_regular() {
1714 let sync = Sync::new("p4", GlobalOpts::new())
1715 .quiet_mode(true)
1716 .limit(5)
1717 .safe_check();
1718
1719 assert_eq!(
1720 args_of(&sync.setup_command("p4")),
1721 ["sync", "-q", "-s", "-m", "5"]
1722 );
1723 }
1724
1725 #[test]
1726 fn transition_to_populate_from_regular() {
1727 let sync = Sync::new("p4", GlobalOpts::new())
1728 .quiet_mode(true)
1729 .limit(5)
1730 .populate();
1731
1732 assert_eq!(
1733 args_of(&sync.setup_command("p4")),
1734 ["sync", "-q", "-p", "-m", "5"]
1735 );
1736 }
1737
1738 #[test]
1739 fn transition_preserves_preview() {
1740 let sync = Sync::new("p4", GlobalOpts::new())
1741 .preview_result()
1742 .quiet_mode(true)
1743 .safe_check();
1744
1745 assert_eq!(
1746 args_of(&sync.setup_command("p4")),
1747 ["sync", "-q", "-s", "-n"]
1748 );
1749 }
1750
1751 #[test]
1752 fn force_mode_blocks_safe_check() {
1753 let sync = Sync::new("p4", GlobalOpts::new())
1756 .force(true)
1757 .quiet_mode(true);
1758
1759 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-q", "-f"]);
1760 }
1761
1762 #[test]
1763 fn all_regular_options_order() {
1764 let sync = Sync::new("p4", GlobalOpts::new())
1765 .script_list_mode(true)
1766 .quiet_mode(true)
1767 .force(true)
1768 .metadata_only(true)
1769 .limit(5)
1770 .parallel(ParallelConfig {
1771 threads: 2,
1772 batch_files: None,
1773 batch_size_bytes: None,
1774 min_files: None,
1775 min_size_bytes: None,
1776 });
1777
1778 #[cfg(not(feature = "lt2022_2"))]
1779 let sync = sync.verify_edge_replication(true);
1780 #[cfg(not(feature = "lt2022_1"))]
1781 let sync = sync.suppress_keyword_expansion(true);
1782 #[cfg(not(feature = "lt2015_1"))]
1783 let sync = sync.reopen_moved_files(true);
1784 #[cfg(not(feature = "lt2022_2"))]
1785 let sync = sync.stream_spec_version(StreamSpecVersion::Current);
1786
1787 let mut expected: Vec<&str> = vec!["sync", "-L", "-q", "-f", "-k"];
1788 #[cfg(not(feature = "lt2022_2"))]
1789 expected.insert(1, "-E");
1790 #[cfg(not(feature = "lt2022_1"))]
1791 {
1792 let k = expected.iter().position(|&a| a == "-L").unwrap() + 1;
1793 expected.insert(k, "-K");
1794 }
1795 #[cfg(not(feature = "lt2015_1"))]
1796 expected.push("-r");
1797 expected.extend(["-m", "5", "--parallel=threads=2"]);
1798 #[cfg(not(feature = "lt2022_2"))]
1799 expected.push("--use-stream-change=0");
1800
1801 assert_eq!(args_of(&sync.setup_command("p4")), expected);
1802 }
1803}