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> ParameterizedSpawn for Sync<M> {
1381 type Input<'a> = &'a [&'a OsStr];
1382 type Output<'a> = Child;
1383 type Error = std::io::Error;
1384
1385 fn spawn_with<'a>(&mut self, files: Self::Input<'a>) -> Result<Self::Output<'a>, Self::Error> {
1389 self.setup_command(&self.bin)
1390 .args(files)
1391 .stdout(Stdio::piped())
1392 .stderr(Stdio::piped())
1393 .spawn()
1394 }
1395}
1396
1397impl<M: ExclusiveOption> Sync<M> {
1398 #[cfg_attr(
1403 feature = "lt2014_2",
1404 doc = "See the [Global Options](GlobalOpts) section."
1405 )]
1406 #[cfg_attr(
1407 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1408 doc = "See the [“Global Options”](GlobalOpts) section."
1409 )]
1410 #[cfg_attr(
1411 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1412 doc = "See [“Global Options”](GlobalOpts)."
1413 )]
1414 #[cfg_attr(
1415 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1416 doc = "See [Global Options](GlobalOpts)."
1417 )]
1418 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1419 pub fn get_global_opts(&self) -> &GlobalOpts {
1420 &self.global_opts
1421 }
1422
1423 #[cfg_attr(
1428 feature = "lt2014_2",
1429 doc = "See the [Global Options](GlobalOpts) section."
1430 )]
1431 #[cfg_attr(
1432 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1433 doc = "See the [“Global Options”](GlobalOpts) section."
1434 )]
1435 #[cfg_attr(
1436 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1437 doc = "See [“Global Options”](GlobalOpts)."
1438 )]
1439 #[cfg_attr(
1440 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1441 doc = "See [Global Options](GlobalOpts)."
1442 )]
1443 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1444 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
1445 self.global_opts = v;
1446 self
1447 }
1448
1449 #[cfg_attr(
1454 feature = "lt2014_2",
1455 doc = "See the [Global Options](GlobalOpts) section."
1456 )]
1457 #[cfg_attr(
1458 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1459 doc = "See the [“Global Options”](GlobalOpts) section."
1460 )]
1461 #[cfg_attr(
1462 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1463 doc = "See [“Global Options”](GlobalOpts)."
1464 )]
1465 #[cfg_attr(
1466 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1467 doc = "See [Global Options](GlobalOpts)."
1468 )]
1469 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1470 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
1471 self.global_opts = v;
1472 self
1473 }
1474}
1475
1476impl<M: ExclusiveOption> SubCommand for Sync<M> {
1477 fn name(&self) -> &str {
1478 "sync"
1479 }
1480
1481 fn inject_local_args(&self, command: &mut Command) {
1482 self.mode.inject_args(command);
1483 }
1484
1485 fn global_opts(&self) -> Option<&GlobalOpts> {
1486 Some(&self.global_opts)
1487 }
1488}
1489
1490#[cfg(test)]
1491mod tests {
1492 use super::*;
1493 use crate::cmd::args_of;
1494
1495 #[test]
1496 fn without_options() {
1497 let sync = Sync::new("p4", GlobalOpts::new());
1498
1499 assert_eq!(args_of(&sync.setup_command("p4")), ["sync"]);
1500 }
1501
1502 #[test]
1503 #[cfg(not(feature = "lt2025_1"))]
1504 fn sync_time_mode() {
1505 let sync = Sync::new("p4", GlobalOpts::new()).sync_time("2024/01/01");
1506
1507 assert_eq!(
1508 args_of(&sync.setup_command("p4")),
1509 ["sync", "-k", "--sync-time=2024/01/01"]
1510 );
1511 }
1512
1513 #[test]
1514 #[cfg(not(feature = "lt2025_1"))]
1515 fn sync_time_mode_epoch() {
1516 let sync = Sync::new("p4", GlobalOpts::new()).sync_time("1700000000");
1517
1518 assert_eq!(
1519 args_of(&sync.setup_command("p4")),
1520 ["sync", "-k", "--sync-time=1700000000"]
1521 );
1522 }
1523
1524 #[test]
1525 #[cfg(not(feature = "lt2025_1"))]
1526 fn sync_time_set_style() {
1527 let mut sync = Sync::new("p4", GlobalOpts::new()).sync_time("2024/01/01");
1528 sync.set_sync_time("2024/06/01");
1529
1530 assert_eq!(sync.get_sync_time(), "2024/06/01");
1531 assert_eq!(
1532 args_of(&sync.setup_command("p4")),
1533 ["sync", "-k", "--sync-time=2024/06/01"]
1534 );
1535 }
1536
1537 #[test]
1538 fn regular_mode_common_options() {
1539 let sync = Sync::new("p4", GlobalOpts::new())
1540 .script_list_mode(true)
1541 .quiet_mode(true)
1542 .limit(5);
1543
1544 #[cfg(not(feature = "lt2022_2"))]
1545 {
1546 let sync = sync.verify_edge_replication(true);
1547 assert_eq!(
1548 args_of(&sync.setup_command("p4")),
1549 ["sync", "-E", "-L", "-q", "-m", "5"]
1550 );
1551 }
1552
1553 #[cfg(feature = "lt2022_2")]
1554 assert_eq!(
1555 args_of(&sync.setup_command("p4")),
1556 ["sync", "-L", "-q", "-m", "5"]
1557 );
1558 }
1559
1560 #[test]
1561 fn regular_mode_force_options() {
1562 let sync = Sync::new("p4", GlobalOpts::new())
1563 .force(true)
1564 .metadata_only(true);
1565
1566 #[cfg(not(feature = "lt2015_1"))]
1567 let sync = sync.reopen_moved_files(true);
1568
1569 #[cfg(not(feature = "lt2015_1"))]
1570 let expected = vec!["sync", "-f", "-k", "-r"];
1571 #[cfg(feature = "lt2015_1")]
1572 let expected = vec!["sync", "-f", "-k"];
1573
1574 assert_eq!(args_of(&sync.setup_command("p4")), expected);
1575 }
1576
1577 #[test]
1578 fn regular_mode_combined() {
1579 let sync = Sync::new("p4", GlobalOpts::new())
1580 .quiet_mode(true)
1581 .force(true)
1582 .limit(10);
1583
1584 assert_eq!(
1585 args_of(&sync.setup_command("p4")),
1586 ["sync", "-q", "-f", "-m", "10"]
1587 );
1588 }
1589
1590 #[test]
1591 fn regular_mode_preview_result() {
1592 let sync = Sync::new("p4", GlobalOpts::new()).preview_result();
1593
1594 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-n"]);
1595 }
1596
1597 #[test]
1598 fn regular_mode_preview_network_traffic() {
1599 let sync = Sync::new("p4", GlobalOpts::new()).preview_network_traffic();
1600
1601 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-N"]);
1602 }
1603
1604 #[test]
1605 fn regular_mode_preview_with_options() {
1606 let sync = Sync::new("p4", GlobalOpts::new())
1607 .quiet_mode(true)
1608 .preview_result()
1609 .limit(3);
1610
1611 assert_eq!(
1612 args_of(&sync.setup_command("p4")),
1613 ["sync", "-q", "-n", "-m", "3"]
1614 );
1615 }
1616
1617 #[test]
1618 fn regular_mode_parallel() {
1619 let sync = Sync::new("p4", GlobalOpts::new()).parallel(ParallelConfig {
1620 threads: 4,
1621 batch_files: Some(8),
1622 batch_size_bytes: None,
1623 min_files: Some(9),
1624 min_size_bytes: None,
1625 });
1626
1627 assert_eq!(
1628 args_of(&sync.setup_command("p4")),
1629 ["sync", "--parallel=threads=4,batch=8,min=9"]
1630 );
1631 }
1632
1633 #[test]
1634 #[cfg(not(feature = "lt2022_2"))]
1635 fn regular_mode_stream_spec_auto() {
1636 let sync = Sync::new("p4", GlobalOpts::new())
1637 .stream_spec_version(StreamSpecVersion::MaxInFilelists);
1638
1639 assert_eq!(
1640 args_of(&sync.setup_command("p4")),
1641 ["sync", "--use-stream-change"]
1642 );
1643 }
1644
1645 #[test]
1646 #[cfg(not(feature = "lt2022_2"))]
1647 fn regular_mode_stream_spec_current() {
1648 let sync =
1649 Sync::new("p4", GlobalOpts::new()).stream_spec_version(StreamSpecVersion::Current);
1650
1651 assert_eq!(
1652 args_of(&sync.setup_command("p4")),
1653 ["sync", "--use-stream-change=0"]
1654 );
1655 }
1656
1657 #[test]
1658 #[cfg(not(feature = "lt2022_2"))]
1659 fn regular_mode_stream_spec_specific() {
1660 let sync = Sync::new("p4", GlobalOpts::new())
1661 .stream_spec_version(StreamSpecVersion::ChangeNumber(123));
1662
1663 assert_eq!(
1664 args_of(&sync.setup_command("p4")),
1665 ["sync", "--use-stream-change=123"]
1666 );
1667 }
1668
1669 #[test]
1670 fn safe_check_mode() {
1671 let sync = Sync::new("p4", GlobalOpts::new()).enable_safe_check();
1672
1673 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-s"]);
1674 }
1675
1676 #[test]
1677 fn safe_check_mode_with_options() {
1678 let sync = Sync::new("p4", GlobalOpts::new())
1679 .enable_safe_check()
1680 .quiet_mode(true)
1681 .limit(5);
1682
1683 assert_eq!(
1684 args_of(&sync.setup_command("p4")),
1685 ["sync", "-q", "-s", "-m", "5"]
1686 );
1687 }
1688
1689 #[test]
1690 fn populate_mode() {
1691 let sync = Sync::new("p4", GlobalOpts::new()).populate_client_workspace();
1692
1693 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-p"]);
1694 }
1695
1696 #[test]
1697 fn populate_mode_with_options() {
1698 let sync = Sync::new("p4", GlobalOpts::new())
1699 .populate_client_workspace()
1700 .quiet_mode(true)
1701 .limit(5);
1702
1703 assert_eq!(
1704 args_of(&sync.setup_command("p4")),
1705 ["sync", "-q", "-p", "-m", "5"]
1706 );
1707 }
1708
1709 #[test]
1710 fn transition_to_safe_check_from_regular() {
1711 let sync = Sync::new("p4", GlobalOpts::new())
1712 .quiet_mode(true)
1713 .limit(5)
1714 .safe_check();
1715
1716 assert_eq!(
1717 args_of(&sync.setup_command("p4")),
1718 ["sync", "-q", "-s", "-m", "5"]
1719 );
1720 }
1721
1722 #[test]
1723 fn transition_to_populate_from_regular() {
1724 let sync = Sync::new("p4", GlobalOpts::new())
1725 .quiet_mode(true)
1726 .limit(5)
1727 .populate();
1728
1729 assert_eq!(
1730 args_of(&sync.setup_command("p4")),
1731 ["sync", "-q", "-p", "-m", "5"]
1732 );
1733 }
1734
1735 #[test]
1736 fn transition_preserves_preview() {
1737 let sync = Sync::new("p4", GlobalOpts::new())
1738 .preview_result()
1739 .quiet_mode(true)
1740 .safe_check();
1741
1742 assert_eq!(
1743 args_of(&sync.setup_command("p4")),
1744 ["sync", "-q", "-s", "-n"]
1745 );
1746 }
1747
1748 #[test]
1749 fn force_mode_blocks_safe_check() {
1750 let sync = Sync::new("p4", GlobalOpts::new())
1753 .force(true)
1754 .quiet_mode(true);
1755
1756 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-q", "-f"]);
1757 }
1758
1759 #[test]
1760 fn all_regular_options_order() {
1761 let sync = Sync::new("p4", GlobalOpts::new())
1762 .script_list_mode(true)
1763 .quiet_mode(true)
1764 .force(true)
1765 .metadata_only(true)
1766 .limit(5)
1767 .parallel(ParallelConfig {
1768 threads: 2,
1769 batch_files: None,
1770 batch_size_bytes: None,
1771 min_files: None,
1772 min_size_bytes: None,
1773 });
1774
1775 #[cfg(not(feature = "lt2022_2"))]
1776 let sync = sync.verify_edge_replication(true);
1777 #[cfg(not(feature = "lt2022_1"))]
1778 let sync = sync.suppress_keyword_expansion(true);
1779 #[cfg(not(feature = "lt2015_1"))]
1780 let sync = sync.reopen_moved_files(true);
1781 #[cfg(not(feature = "lt2022_2"))]
1782 let sync = sync.stream_spec_version(StreamSpecVersion::Current);
1783
1784 let mut expected: Vec<&str> = vec!["sync", "-L", "-q", "-f", "-k"];
1785 #[cfg(not(feature = "lt2022_2"))]
1786 expected.insert(1, "-E");
1787 #[cfg(not(feature = "lt2022_1"))]
1788 {
1789 let k = expected.iter().position(|&a| a == "-L").unwrap() + 1;
1790 expected.insert(k, "-K");
1791 }
1792 #[cfg(not(feature = "lt2015_1"))]
1793 expected.push("-r");
1794 expected.extend(["-m", "5", "--parallel=threads=2"]);
1795 #[cfg(not(feature = "lt2022_2"))]
1796 expected.push("--use-stream-change=0");
1797
1798 assert_eq!(args_of(&sync.setup_command("p4")), expected);
1799 }
1800}