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#[cfg_attr(feature = "lt2025_1", doc = ".")]
301#[cfg_attr(
302 not(feature = "lt2025_1"),
303 doc = "; [`Self::sync_time`] moves into [`SyncTimeMode`]."
304)]
305#[derive(Debug, Clone, Default)]
306pub struct Sync<M = Unselected> {
307 bin: PathBuf,
308
309 global_opts: GlobalOpts,
310
311 mode: M,
312}
313
314impl Sync<Unselected> {
315 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
319 Self {
320 bin: bin.into(),
321 global_opts,
322 mode: Unselected,
323 }
324 }
325
326 #[cfg_attr(
333 all(feature = "lt2025_2", not(feature = "lt2025_1")),
334 doc = "The value of `N` can be Unix epoch time or the perforce date",
335 doc = "time format."
336 )]
337 #[cfg_attr(
338 not(feature = "lt2025_2"),
339 doc = "The value of `N` can be Unix epoch time or the Perforce date",
340 doc = "time format."
341 )]
342 #[cfg(not(feature = "lt2025_1"))]
346 pub fn sync_time(self, time: impl Into<String>) -> Sync<SyncTimeMode> {
347 Sync {
348 bin: self.bin,
349 global_opts: self.global_opts,
350 mode: SyncTimeMode {
351 sync_time: time.into(),
352 },
353 }
354 }
355
356 #[cfg_attr(
363 feature = "lt2017_2",
364 doc = "If the file was modified outside of Perforce control, an error",
365 doc = "message is displayed and the file is not overwritten."
366 )]
367 #[cfg_attr(
368 all(feature = "lt2024_1", not(feature = "lt2017_2")),
369 doc = "If the file was modified outside of the control of Helix",
370 doc = "Server, an error message is displayed and the file is not",
371 doc = "overwritten."
372 )]
373 #[cfg_attr(
374 all(feature = "lt2024_2", not(feature = "lt2024_1")),
375 doc = "If the file was modified outside of the control of Helix Core",
376 doc = "Server, an error message is displayed and the file is not",
377 doc = "overwritten."
378 )]
379 #[cfg_attr(
380 not(feature = "lt2024_2"),
381 doc = "If the file was modified outside of the control of P4 Server,",
382 doc = "an error message is displayed and the file is not overwritten."
383 )]
384 pub fn enable_safe_check(self) -> Sync<RegularMode<SafeCheckMode>> {
388 Sync {
389 bin: self.bin,
390 global_opts: self.global_opts,
391 mode: RegularMode {
392 mode: SafeCheckMode,
393 ..RegularMode::default()
394 },
395 }
396 }
397
398 pub fn populate_client_workspace(self) -> Sync<RegularMode<PopulateMode>> {
409 Sync {
410 bin: self.bin,
411 global_opts: self.global_opts,
412 mode: RegularMode {
413 mode: PopulateMode,
414 ..RegularMode::default()
415 },
416 }
417 }
418
419 #[cfg(not(feature = "lt2022_2"))]
429 pub fn verify_edge_replication(self, v: bool) -> Sync<RegularMode> {
430 Sync {
431 bin: self.bin,
432 global_opts: self.global_opts,
433 mode: RegularMode {
434 verify_edge_replication: v,
435 ..RegularMode::default()
436 },
437 }
438 }
439
440 #[cfg_attr(
447 not(feature = "lt2016_1"),
448 doc = "",
449 doc = "When this flag is used, the arguments are processed together by",
450 doc = "building an internal table similar to a label. This file list",
451 doc = "processing is significantly faster than having to call the",
452 doc = "internal query engine for each individual file argument. However,",
453 doc = "the file argument syntax is strict and the command will not run",
454 doc = "if an error is encountered."
455 )]
456 pub fn script_list_mode(self, v: bool) -> Sync<RegularMode> {
459 Sync {
460 bin: self.bin,
461 global_opts: self.global_opts,
462 mode: RegularMode {
463 script_list_mode: v,
464 ..RegularMode::default()
465 },
466 }
467 }
468
469 #[cfg_attr(feature = "lt2024_2", doc = "See File type modifiers.")]
476 #[cfg_attr(
477 not(feature = "lt2024_2"),
478 doc = "To learn more, see File type modifiers."
479 )]
480 #[cfg(not(feature = "lt2022_1"))]
483 pub fn suppress_keyword_expansion(self, v: bool) -> Sync<RegularMode> {
484 Sync {
485 bin: self.bin,
486 global_opts: self.global_opts,
487 mode: RegularMode {
488 suppress_keyword_expansion: v,
489 ..RegularMode::default()
490 },
491 }
492 }
493
494 pub fn quiet_mode(self, v: bool) -> Sync<RegularMode> {
503 Sync {
504 bin: self.bin,
505 global_opts: self.global_opts,
506 mode: RegularMode {
507 quiet_mode: v,
508 ..RegularMode::default()
509 },
510 }
511 }
512
513 #[cfg_attr(
519 not(feature = "lt2022_2"),
520 doc = "",
521 doc = "This option is useful in conjunction with tagged output and the",
522 doc = "`-n` flag, to preview how many files will be synced without",
523 doc = "transferring all the file data."
524 )]
525 pub fn limit(self, v: u64) -> Sync<RegularMode> {
528 Sync {
529 bin: self.bin,
530 global_opts: self.global_opts,
531 mode: RegularMode {
532 limit: Some(v),
533 ..RegularMode::default()
534 },
535 }
536 }
537
538 pub fn parallel(self, v: ParallelConfig) -> Sync<RegularMode> {
546 Sync {
547 bin: self.bin,
548 global_opts: self.global_opts,
549 mode: RegularMode {
550 parallel: Some(v),
551 ..RegularMode::default()
552 },
553 }
554 }
555
556 #[cfg(not(feature = "lt2022_2"))]
565 pub fn stream_spec_version(self, v: StreamSpecVersion) -> Sync<RegularMode> {
566 Sync {
567 bin: self.bin,
568 global_opts: self.global_opts,
569 mode: RegularMode {
570 stream_spec_version: Some(v),
571 ..RegularMode::default()
572 },
573 }
574 }
575
576 #[cfg(not(feature = "lt2022_2"))]
581 pub fn sc_max_change_number(self) -> Sync<RegularMode> {
582 self.stream_spec_version(StreamSpecVersion::MaxInFilelists)
583 }
584
585 #[cfg(not(feature = "lt2022_2"))]
589 pub fn sc_current_stream_spec(self) -> Sync<RegularMode> {
590 self.stream_spec_version(StreamSpecVersion::Current)
591 }
592
593 #[cfg(not(feature = "lt2022_2"))]
598 pub fn sc_change_number(self, n: u32) -> Sync<RegularMode> {
599 self.stream_spec_version(StreamSpecVersion::ChangeNumber(n))
600 }
601
602 #[cfg_attr(
607 feature = "lt2016_1",
608 doc = "Display the results of the sync without actually performing the",
609 doc = "sync.",
610 doc = "",
611 doc = "This lets you make sure that the sync does what you think it",
612 doc = "does before you do it."
613 )]
614 #[cfg_attr(
615 not(feature = "lt2016_1"),
616 doc = "Preview mode: display the results of the sync without actually",
617 doc = "performing the sync."
618 )]
619 pub fn preview_result(self) -> Sync<RegularMode<Unselected, PreviewResult>> {
623 Sync {
624 bin: self.bin,
625 global_opts: self.global_opts,
626 mode: RegularMode {
627 preview: PreviewResult,
628 ..RegularMode::default()
629 },
630 }
631 }
632
633 #[cfg_attr(
638 feature = "lt2016_1",
639 doc = "Display a summary of the expected network traffic associated",
640 doc = "with a sync, without performing the sync."
641 )]
642 #[cfg_attr(
643 all(feature = "lt2021_2", not(feature = "lt2016_1")),
644 doc = "Preview mode: display a summary of the expected network traffic",
645 doc = "associated with a sync, without performing the sync."
646 )]
647 #[cfg_attr(
648 not(feature = "lt2021_2"),
649 doc = "Preview mode: display a summary of the expected network traffic",
650 doc = "associated with a sync, without performing the sync.",
651 doc = "",
652 doc = "This tells you how many files are to be added or updated, which",
653 doc = "is useful if there are many large files, limits on bandwidth, or",
654 doc = "limits on disk space."
655 )]
656 pub fn preview_network_traffic(self) -> Sync<RegularMode<Unselected, PreviewNetworkTraffic>> {
660 Sync {
661 bin: self.bin,
662 global_opts: self.global_opts,
663 mode: RegularMode {
664 preview: PreviewNetworkTraffic,
665 ..RegularMode::default()
666 },
667 }
668 }
669
670 #[cfg_attr(
675 feature = "lt2014_2",
676 doc = "Force the sync. Perforce performs the sync even if the client",
677 doc = "workspace already has the file at the specified revision. If the",
678 doc = "file is writable, it is overwritten.",
679 doc = "",
680 doc = "This flag does not affect open files, but it does override the",
681 doc = "noclobber client option."
682 )]
683 #[cfg_attr(
684 all(feature = "lt2017_2", not(feature = "lt2014_2")),
685 doc = "Force the sync. Perforce performs the sync even if the client",
686 doc = "workspace already has the file at the specified revision. If the",
687 doc = "file is writable, it is overwritten.",
688 doc = "",
689 doc = "This option does not affect open files, but it does override the",
690 doc = "noclobber client option."
691 )]
692 #[cfg_attr(
693 all(feature = "lt2024_1", not(feature = "lt2017_2")),
694 doc = "Force the sync. Helix Server performs the sync even if the",
695 doc = "client workspace already has the file at the specified",
696 doc = "revision. If the file is writable, it is overwritten.",
697 doc = "",
698 doc = "This option does not affect open files, but it does override the",
699 doc = "noclobber client option (see p4 client)."
700 )]
701 #[cfg_attr(
702 all(feature = "lt2024_2", not(feature = "lt2024_1")),
703 doc = "Force the sync. Helix Core Server performs the sync even if the",
704 doc = "client workspace already has the file at the specified",
705 doc = "revision. If the file is writable, it is overwritten.",
706 doc = "",
707 doc = "This option does not affect open files, but it does override the",
708 doc = "noclobber client option (see p4 client)."
709 )]
710 #[cfg_attr(
711 not(feature = "lt2024_2"),
712 doc = "Force the sync. P4 Server performs the sync even if the client",
713 doc = "workspace already has the file at the specified revision. If the",
714 doc = "file is writable, it is overwritten.",
715 doc = "",
716 doc = "This option does not affect open files, but it does override the",
717 doc = "noclobber client option (see p4 client)."
718 )]
719 pub fn force(self, v: bool) -> Sync<RegularMode<ForceRegularMode>> {
724 Sync {
725 bin: self.bin,
726 global_opts: self.global_opts,
727 mode: RegularMode {
728 mode: ForceRegularMode {
729 force: v,
730 ..ForceRegularMode::default()
731 },
732 ..RegularMode::default()
733 },
734 }
735 }
736
737 #[cfg_attr(
742 feature = "lt2022_2",
743 doc = "Keep existing workspace files; update the have list without",
744 doc = "updating the client workspace."
745 )]
746 #[cfg_attr(
747 not(feature = "lt2022_2"),
748 doc = "Update server metadata without syncing files. Keep existing",
749 doc = "workspace files and update the have list without updating the",
750 doc = "client workspace."
751 )]
752 pub fn metadata_only(self, v: bool) -> Sync<RegularMode<ForceRegularMode>> {
757 Sync {
758 bin: self.bin,
759 global_opts: self.global_opts,
760 mode: RegularMode {
761 mode: ForceRegularMode {
762 metadata_only: v,
763 ..ForceRegularMode::default()
764 },
765 ..RegularMode::default()
766 },
767 }
768 }
769
770 #[cfg(not(feature = "lt2015_1"))]
781 pub fn reopen_moved_files(self, v: bool) -> Sync<RegularMode<ForceRegularMode>> {
782 Sync {
783 bin: self.bin,
784 global_opts: self.global_opts,
785 mode: RegularMode {
786 mode: ForceRegularMode {
787 reopen_moved_files: v,
788 ..ForceRegularMode::default()
789 },
790 ..RegularMode::default()
791 },
792 }
793 }
794}
795
796impl<Mode: ExclusiveOption, P: ExclusiveOption> Sync<RegularMode<Mode, P>> {
799 #[cfg(not(feature = "lt2022_2"))]
801 pub fn get_verify_edge_replication(&self) -> bool {
802 self.mode.verify_edge_replication
803 }
804
805 #[cfg(not(feature = "lt2022_2"))]
807 pub fn set_verify_edge_replication(&mut self, v: bool) -> &mut Self {
808 self.mode.verify_edge_replication = v;
809 self
810 }
811
812 #[cfg(not(feature = "lt2022_2"))]
814 pub fn verify_edge_replication(mut self, v: bool) -> Self {
815 self.mode.verify_edge_replication = v;
816 self
817 }
818
819 pub fn get_script_list_mode(&self) -> bool {
821 self.mode.script_list_mode
822 }
823
824 pub fn set_script_list_mode(&mut self, v: bool) -> &mut Self {
826 self.mode.script_list_mode = v;
827 self
828 }
829
830 pub fn script_list_mode(mut self, v: bool) -> Self {
832 self.mode.script_list_mode = v;
833 self
834 }
835
836 #[cfg(not(feature = "lt2022_1"))]
838 pub fn get_suppress_keyword_expansion(&self) -> bool {
839 self.mode.suppress_keyword_expansion
840 }
841
842 #[cfg(not(feature = "lt2022_1"))]
844 pub fn set_suppress_keyword_expansion(&mut self, v: bool) -> &mut Self {
845 self.mode.suppress_keyword_expansion = v;
846 self
847 }
848
849 #[cfg(not(feature = "lt2022_1"))]
851 pub fn suppress_keyword_expansion(mut self, v: bool) -> Self {
852 self.mode.suppress_keyword_expansion = v;
853 self
854 }
855
856 pub fn get_quiet_mode(&self) -> bool {
858 self.mode.quiet_mode
859 }
860
861 pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
863 self.mode.quiet_mode = v;
864 self
865 }
866
867 pub fn quiet_mode(mut self, v: bool) -> Self {
869 self.mode.quiet_mode = v;
870 self
871 }
872
873 pub fn get_limit(&self) -> Option<u64> {
875 self.mode.limit
876 }
877
878 pub fn set_limit(&mut self, v: u64) -> &mut Self {
880 self.mode.limit = Some(v);
881 self
882 }
883
884 pub fn limit(mut self, v: u64) -> Self {
886 self.mode.limit = Some(v);
887 self
888 }
889
890 pub fn get_parallel(&self) -> Option<&ParallelConfig> {
892 self.mode.parallel.as_ref()
893 }
894
895 pub fn set_parallel(&mut self, v: ParallelConfig) -> &mut Self {
897 self.mode.parallel = Some(v);
898 self
899 }
900
901 pub fn parallel(mut self, v: ParallelConfig) -> Self {
903 self.mode.parallel = Some(v);
904 self
905 }
906
907 #[cfg(not(feature = "lt2022_2"))]
909 pub fn get_stream_spec_version(&self) -> Option<StreamSpecVersion> {
910 self.mode.stream_spec_version
911 }
912
913 #[cfg(not(feature = "lt2022_2"))]
915 pub fn set_stream_spec_version(&mut self, v: StreamSpecVersion) -> &mut Self {
916 self.mode.stream_spec_version = Some(v);
917 self
918 }
919
920 #[cfg(not(feature = "lt2022_2"))]
922 pub fn stream_spec_version(mut self, v: StreamSpecVersion) -> Self {
923 self.mode.stream_spec_version = Some(v);
924 self
925 }
926
927 #[cfg(not(feature = "lt2022_2"))]
930 pub fn set_sc_max_change_number(&mut self) -> &mut Self {
931 self.mode.stream_spec_version = Some(StreamSpecVersion::MaxInFilelists);
932 self
933 }
934
935 #[cfg(not(feature = "lt2022_2"))]
938 pub fn sc_max_change_number(mut self) -> Self {
939 self.mode.stream_spec_version = Some(StreamSpecVersion::MaxInFilelists);
940 self
941 }
942
943 #[cfg(not(feature = "lt2022_2"))]
945 pub fn set_sc_current_stream_spec(&mut self) -> &mut Self {
946 self.mode.stream_spec_version = Some(StreamSpecVersion::Current);
947 self
948 }
949
950 #[cfg(not(feature = "lt2022_2"))]
952 pub fn sc_current_stream_spec(mut self) -> Self {
953 self.mode.stream_spec_version = Some(StreamSpecVersion::Current);
954 self
955 }
956
957 #[cfg(not(feature = "lt2022_2"))]
960 pub fn set_sc_change_number(&mut self, n: u32) -> &mut Self {
961 self.mode.stream_spec_version = Some(StreamSpecVersion::ChangeNumber(n));
962 self
963 }
964
965 #[cfg(not(feature = "lt2022_2"))]
968 pub fn sc_change_number(mut self, n: u32) -> Self {
969 self.mode.stream_spec_version = Some(StreamSpecVersion::ChangeNumber(n));
970 self
971 }
972}
973
974impl<P: ExclusiveOption> Sync<RegularMode<Unselected, P>> {
977 #[cfg_attr(
982 feature = "lt2014_2",
983 doc = "Force the sync. Perforce performs the sync even if the client",
984 doc = "workspace already has the file at the specified revision. If the",
985 doc = "file is writable, it is overwritten.",
986 doc = "",
987 doc = "This flag does not affect open files, but it does override the",
988 doc = "noclobber client option."
989 )]
990 #[cfg_attr(
991 all(feature = "lt2017_2", not(feature = "lt2014_2")),
992 doc = "Force the sync. Perforce performs the sync even if the client",
993 doc = "workspace already has the file at the specified revision. If the",
994 doc = "file is writable, it is overwritten.",
995 doc = "",
996 doc = "This option does not affect open files, but it does override the",
997 doc = "noclobber client option."
998 )]
999 #[cfg_attr(
1000 all(feature = "lt2024_1", not(feature = "lt2017_2")),
1001 doc = "Force the sync. Helix Server performs the sync even if the",
1002 doc = "client workspace already has the file at the specified",
1003 doc = "revision. If the file is writable, it is overwritten.",
1004 doc = "",
1005 doc = "This option does not affect open files, but it does override the",
1006 doc = "noclobber client option (see p4 client)."
1007 )]
1008 #[cfg_attr(
1009 all(feature = "lt2024_2", not(feature = "lt2024_1")),
1010 doc = "Force the sync. Helix Core Server performs the sync even if the",
1011 doc = "client workspace already has the file at the specified",
1012 doc = "revision. If the file is writable, it is overwritten.",
1013 doc = "",
1014 doc = "This option does not affect open files, but it does override the",
1015 doc = "noclobber client option (see p4 client)."
1016 )]
1017 #[cfg_attr(
1018 not(feature = "lt2024_2"),
1019 doc = "Force the sync. P4 Server performs the sync even if the client",
1020 doc = "workspace already has the file at the specified revision. If the",
1021 doc = "file is writable, it is overwritten.",
1022 doc = "",
1023 doc = "This option does not affect open files, but it does override the",
1024 doc = "noclobber client option (see p4 client)."
1025 )]
1026 pub fn force(self, v: bool) -> Sync<RegularMode<ForceRegularMode, P>> {
1030 Sync {
1031 bin: self.bin,
1032 global_opts: self.global_opts,
1033 mode: RegularMode {
1034 #[cfg(not(feature = "lt2022_2"))]
1035 verify_edge_replication: self.mode.verify_edge_replication,
1036 script_list_mode: self.mode.script_list_mode,
1037 #[cfg(not(feature = "lt2022_1"))]
1038 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1039 quiet_mode: self.mode.quiet_mode,
1040 limit: self.mode.limit,
1041 parallel: self.mode.parallel,
1042 #[cfg(not(feature = "lt2022_2"))]
1043 stream_spec_version: self.mode.stream_spec_version,
1044 mode: ForceRegularMode {
1045 force: v,
1046 ..ForceRegularMode::default()
1047 },
1048 preview: self.mode.preview,
1049 },
1050 }
1051 }
1052
1053 #[cfg_attr(
1058 feature = "lt2022_2",
1059 doc = "Keep existing workspace files; update the have list without",
1060 doc = "updating the client workspace."
1061 )]
1062 #[cfg_attr(
1063 not(feature = "lt2022_2"),
1064 doc = "Update server metadata without syncing files. Keep existing",
1065 doc = "workspace files and update the have list without updating the",
1066 doc = "client workspace."
1067 )]
1068 pub fn metadata_only(self, v: bool) -> Sync<RegularMode<ForceRegularMode, P>> {
1072 Sync {
1073 bin: self.bin,
1074 global_opts: self.global_opts,
1075 mode: RegularMode {
1076 #[cfg(not(feature = "lt2022_2"))]
1077 verify_edge_replication: self.mode.verify_edge_replication,
1078 script_list_mode: self.mode.script_list_mode,
1079 #[cfg(not(feature = "lt2022_1"))]
1080 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1081 quiet_mode: self.mode.quiet_mode,
1082 limit: self.mode.limit,
1083 parallel: self.mode.parallel,
1084 #[cfg(not(feature = "lt2022_2"))]
1085 stream_spec_version: self.mode.stream_spec_version,
1086 mode: ForceRegularMode {
1087 metadata_only: v,
1088 ..ForceRegularMode::default()
1089 },
1090 preview: self.mode.preview,
1091 },
1092 }
1093 }
1094
1095 #[cfg(not(feature = "lt2015_1"))]
1105 pub fn reopen_moved_files(self, v: bool) -> Sync<RegularMode<ForceRegularMode, P>> {
1106 Sync {
1107 bin: self.bin,
1108 global_opts: self.global_opts,
1109 mode: RegularMode {
1110 #[cfg(not(feature = "lt2022_2"))]
1111 verify_edge_replication: self.mode.verify_edge_replication,
1112 script_list_mode: self.mode.script_list_mode,
1113 #[cfg(not(feature = "lt2022_1"))]
1114 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1115 quiet_mode: self.mode.quiet_mode,
1116 limit: self.mode.limit,
1117 parallel: self.mode.parallel,
1118 #[cfg(not(feature = "lt2022_2"))]
1119 stream_spec_version: self.mode.stream_spec_version,
1120 mode: ForceRegularMode {
1121 reopen_moved_files: v,
1122 ..ForceRegularMode::default()
1123 },
1124 preview: self.mode.preview,
1125 },
1126 }
1127 }
1128
1129 #[cfg_attr(
1136 feature = "lt2017_2",
1137 doc = "If the file was modified outside of Perforce control, an error",
1138 doc = "message is displayed and the file is not overwritten."
1139 )]
1140 #[cfg_attr(
1141 all(feature = "lt2024_1", not(feature = "lt2017_2")),
1142 doc = "If the file was modified outside of the control of Helix",
1143 doc = "Server, an error message is displayed and the file is not",
1144 doc = "overwritten."
1145 )]
1146 #[cfg_attr(
1147 all(feature = "lt2024_2", not(feature = "lt2024_1")),
1148 doc = "If the file was modified outside of the control of Helix Core",
1149 doc = "Server, an error message is displayed and the file is not",
1150 doc = "overwritten."
1151 )]
1152 #[cfg_attr(
1153 not(feature = "lt2024_2"),
1154 doc = "If the file was modified outside of the control of P4 Server,",
1155 doc = "an error message is displayed and the file is not overwritten."
1156 )]
1157 pub fn safe_check(self) -> Sync<RegularMode<SafeCheckMode, P>> {
1160 Sync {
1161 bin: self.bin,
1162 global_opts: self.global_opts,
1163 mode: RegularMode {
1164 #[cfg(not(feature = "lt2022_2"))]
1165 verify_edge_replication: self.mode.verify_edge_replication,
1166 script_list_mode: self.mode.script_list_mode,
1167 #[cfg(not(feature = "lt2022_1"))]
1168 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1169 quiet_mode: self.mode.quiet_mode,
1170 limit: self.mode.limit,
1171 parallel: self.mode.parallel,
1172 #[cfg(not(feature = "lt2022_2"))]
1173 stream_spec_version: self.mode.stream_spec_version,
1174 mode: SafeCheckMode,
1175 preview: self.mode.preview,
1176 },
1177 }
1178 }
1179
1180 pub fn populate(self) -> Sync<RegularMode<PopulateMode, P>> {
1190 Sync {
1191 bin: self.bin,
1192 global_opts: self.global_opts,
1193 mode: RegularMode {
1194 #[cfg(not(feature = "lt2022_2"))]
1195 verify_edge_replication: self.mode.verify_edge_replication,
1196 script_list_mode: self.mode.script_list_mode,
1197 #[cfg(not(feature = "lt2022_1"))]
1198 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1199 quiet_mode: self.mode.quiet_mode,
1200 limit: self.mode.limit,
1201 parallel: self.mode.parallel,
1202 #[cfg(not(feature = "lt2022_2"))]
1203 stream_spec_version: self.mode.stream_spec_version,
1204 mode: PopulateMode,
1205 preview: self.mode.preview,
1206 },
1207 }
1208 }
1209}
1210
1211impl<Mode: ExclusiveOption> Sync<RegularMode<Mode, Unselected>> {
1214 #[cfg_attr(
1219 feature = "lt2016_1",
1220 doc = "Display the results of the sync without actually performing the",
1221 doc = "sync.",
1222 doc = "",
1223 doc = "This lets you make sure that the sync does what you think it",
1224 doc = "does before you do it."
1225 )]
1226 #[cfg_attr(
1227 not(feature = "lt2016_1"),
1228 doc = "Preview mode: display the results of the sync without actually",
1229 doc = "performing the sync."
1230 )]
1231 pub fn preview_result(self) -> Sync<RegularMode<Mode, PreviewResult>> {
1234 Sync {
1235 bin: self.bin,
1236 global_opts: self.global_opts,
1237 mode: RegularMode {
1238 #[cfg(not(feature = "lt2022_2"))]
1239 verify_edge_replication: self.mode.verify_edge_replication,
1240 script_list_mode: self.mode.script_list_mode,
1241 #[cfg(not(feature = "lt2022_1"))]
1242 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1243 quiet_mode: self.mode.quiet_mode,
1244 limit: self.mode.limit,
1245 parallel: self.mode.parallel,
1246 #[cfg(not(feature = "lt2022_2"))]
1247 stream_spec_version: self.mode.stream_spec_version,
1248 mode: self.mode.mode,
1249 preview: PreviewResult,
1250 },
1251 }
1252 }
1253
1254 #[cfg_attr(
1259 feature = "lt2016_1",
1260 doc = "Display a summary of the expected network traffic associated",
1261 doc = "with a sync, without performing the sync."
1262 )]
1263 #[cfg_attr(
1264 all(feature = "lt2021_2", not(feature = "lt2016_1")),
1265 doc = "Preview mode: display a summary of the expected network traffic",
1266 doc = "associated with a sync, without performing the sync."
1267 )]
1268 #[cfg_attr(
1269 not(feature = "lt2021_2"),
1270 doc = "Preview mode: display a summary of the expected network traffic",
1271 doc = "associated with a sync, without performing the sync.",
1272 doc = "",
1273 doc = "This tells you how many files are to be added or updated, which",
1274 doc = "is useful if there are many large files, limits on bandwidth, or",
1275 doc = "limits on disk space."
1276 )]
1277 pub fn preview_network_traffic(self) -> Sync<RegularMode<Mode, PreviewNetworkTraffic>> {
1280 Sync {
1281 bin: self.bin,
1282 global_opts: self.global_opts,
1283 mode: RegularMode {
1284 #[cfg(not(feature = "lt2022_2"))]
1285 verify_edge_replication: self.mode.verify_edge_replication,
1286 script_list_mode: self.mode.script_list_mode,
1287 #[cfg(not(feature = "lt2022_1"))]
1288 suppress_keyword_expansion: self.mode.suppress_keyword_expansion,
1289 quiet_mode: self.mode.quiet_mode,
1290 limit: self.mode.limit,
1291 parallel: self.mode.parallel,
1292 #[cfg(not(feature = "lt2022_2"))]
1293 stream_spec_version: self.mode.stream_spec_version,
1294 mode: self.mode.mode,
1295 preview: PreviewNetworkTraffic,
1296 },
1297 }
1298 }
1299}
1300
1301impl<P: ExclusiveOption> Sync<RegularMode<ForceRegularMode, P>> {
1304 pub fn get_force(&self) -> bool {
1306 self.mode.mode.force
1307 }
1308
1309 pub fn set_force(&mut self, v: bool) -> &mut Self {
1311 self.mode.mode.force = v;
1312 self
1313 }
1314
1315 pub fn force(mut self, v: bool) -> Self {
1317 self.mode.mode.force = v;
1318 self
1319 }
1320
1321 pub fn get_metadata_only(&self) -> bool {
1323 self.mode.mode.metadata_only
1324 }
1325
1326 pub fn set_metadata_only(&mut self, v: bool) -> &mut Self {
1328 self.mode.mode.metadata_only = v;
1329 self
1330 }
1331
1332 pub fn metadata_only(mut self, v: bool) -> Self {
1334 self.mode.mode.metadata_only = v;
1335 self
1336 }
1337
1338 #[cfg(not(feature = "lt2015_1"))]
1340 pub fn get_reopen_moved_files(&self) -> bool {
1341 self.mode.mode.reopen_moved_files
1342 }
1343
1344 #[cfg(not(feature = "lt2015_1"))]
1346 pub fn set_reopen_moved_files(&mut self, v: bool) -> &mut Self {
1347 self.mode.mode.reopen_moved_files = v;
1348 self
1349 }
1350
1351 #[cfg(not(feature = "lt2015_1"))]
1353 pub fn reopen_moved_files(mut self, v: bool) -> Self {
1354 self.mode.mode.reopen_moved_files = v;
1355 self
1356 }
1357}
1358
1359#[cfg(not(feature = "lt2025_1"))]
1362impl Sync<SyncTimeMode> {
1363 pub fn get_sync_time(&self) -> &str {
1365 &self.mode.sync_time
1366 }
1367
1368 pub fn set_sync_time(&mut self, v: impl Into<String>) -> &mut Self {
1371 self.mode.sync_time = v.into();
1372 self
1373 }
1374
1375 pub fn sync_time(mut self, v: impl Into<String>) -> Self {
1378 self.mode.sync_time = v.into();
1379 self
1380 }
1381}
1382
1383impl<M: ExclusiveOption, S, I> ParameterizedSpawn<(S,)> for Sync<M>
1386where
1387 S: IntoIterator<Item = I>,
1388 I: AsRef<OsStr>,
1389{
1390 type Output = Child;
1391 type Error = std::io::Error;
1392
1393 fn spawn_with(&mut self, (files,): (S,)) -> Result<Self::Output, Self::Error> {
1397 self.setup_command(&self.bin)
1398 .args(files)
1399 .stdout(Stdio::piped())
1400 .stderr(Stdio::piped())
1401 .spawn()
1402 }
1403}
1404
1405impl<M: ExclusiveOption> Sync<M> {
1406 #[cfg_attr(
1411 feature = "lt2014_2",
1412 doc = "See the [Global Options](GlobalOpts) section."
1413 )]
1414 #[cfg_attr(
1415 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1416 doc = "See the [“Global Options”](GlobalOpts) section."
1417 )]
1418 #[cfg_attr(
1419 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1420 doc = "See [“Global Options”](GlobalOpts)."
1421 )]
1422 #[cfg_attr(
1423 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1424 doc = "See [Global Options](GlobalOpts)."
1425 )]
1426 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1427 pub fn get_global_opts(&self) -> &GlobalOpts {
1428 &self.global_opts
1429 }
1430
1431 #[cfg_attr(
1436 feature = "lt2014_2",
1437 doc = "See the [Global Options](GlobalOpts) section."
1438 )]
1439 #[cfg_attr(
1440 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1441 doc = "See the [“Global Options”](GlobalOpts) section."
1442 )]
1443 #[cfg_attr(
1444 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1445 doc = "See [“Global Options”](GlobalOpts)."
1446 )]
1447 #[cfg_attr(
1448 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1449 doc = "See [Global Options](GlobalOpts)."
1450 )]
1451 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1452 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
1453 self.global_opts = v;
1454 self
1455 }
1456
1457 #[cfg_attr(
1462 feature = "lt2014_2",
1463 doc = "See the [Global Options](GlobalOpts) section."
1464 )]
1465 #[cfg_attr(
1466 all(feature = "lt2015_1", not(feature = "lt2014_2")),
1467 doc = "See the [“Global Options”](GlobalOpts) section."
1468 )]
1469 #[cfg_attr(
1470 all(feature = "lt2017_1", not(feature = "lt2015_1")),
1471 doc = "See [“Global Options”](GlobalOpts)."
1472 )]
1473 #[cfg_attr(
1474 all(feature = "lt2018_2", not(feature = "lt2017_1")),
1475 doc = "See [Global Options](GlobalOpts)."
1476 )]
1477 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
1478 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
1479 self.global_opts = v;
1480 self
1481 }
1482}
1483
1484impl<M: ExclusiveOption> SubCommand for Sync<M> {
1485 fn name(&self) -> &str {
1486 "sync"
1487 }
1488
1489 fn inject_local_args(&self, command: &mut Command) {
1490 self.mode.inject_args(command);
1491 }
1492
1493 fn global_opts(&self) -> Option<&GlobalOpts> {
1494 Some(&self.global_opts)
1495 }
1496}
1497
1498#[cfg(test)]
1499mod tests {
1500 use super::*;
1501 use crate::cmd::args_of;
1502
1503 #[test]
1504 fn without_options() {
1505 let sync = Sync::new("p4", GlobalOpts::new());
1506
1507 assert_eq!(args_of(&sync.setup_command("p4")), ["sync"]);
1508 }
1509
1510 #[test]
1511 #[cfg(not(feature = "lt2025_1"))]
1512 fn sync_time_mode() {
1513 let sync = Sync::new("p4", GlobalOpts::new()).sync_time("2024/01/01");
1514
1515 assert_eq!(
1516 args_of(&sync.setup_command("p4")),
1517 ["sync", "-k", "--sync-time=2024/01/01"]
1518 );
1519 }
1520
1521 #[test]
1522 #[cfg(not(feature = "lt2025_1"))]
1523 fn sync_time_mode_epoch() {
1524 let sync = Sync::new("p4", GlobalOpts::new()).sync_time("1700000000");
1525
1526 assert_eq!(
1527 args_of(&sync.setup_command("p4")),
1528 ["sync", "-k", "--sync-time=1700000000"]
1529 );
1530 }
1531
1532 #[test]
1533 #[cfg(not(feature = "lt2025_1"))]
1534 fn sync_time_set_style() {
1535 let mut sync = Sync::new("p4", GlobalOpts::new()).sync_time("2024/01/01");
1536 sync.set_sync_time("2024/06/01");
1537
1538 assert_eq!(sync.get_sync_time(), "2024/06/01");
1539 assert_eq!(
1540 args_of(&sync.setup_command("p4")),
1541 ["sync", "-k", "--sync-time=2024/06/01"]
1542 );
1543 }
1544
1545 #[test]
1546 fn regular_mode_common_options() {
1547 let sync = Sync::new("p4", GlobalOpts::new())
1548 .script_list_mode(true)
1549 .quiet_mode(true)
1550 .limit(5);
1551
1552 #[cfg(not(feature = "lt2022_2"))]
1553 {
1554 let sync = sync.verify_edge_replication(true);
1555 assert_eq!(
1556 args_of(&sync.setup_command("p4")),
1557 ["sync", "-E", "-L", "-q", "-m", "5"]
1558 );
1559 }
1560
1561 #[cfg(feature = "lt2022_2")]
1562 assert_eq!(
1563 args_of(&sync.setup_command("p4")),
1564 ["sync", "-L", "-q", "-m", "5"]
1565 );
1566 }
1567
1568 #[test]
1569 fn regular_mode_force_options() {
1570 let sync = Sync::new("p4", GlobalOpts::new())
1571 .force(true)
1572 .metadata_only(true);
1573
1574 #[cfg(not(feature = "lt2015_1"))]
1575 let sync = sync.reopen_moved_files(true);
1576
1577 #[cfg(not(feature = "lt2015_1"))]
1578 let expected = vec!["sync", "-f", "-k", "-r"];
1579 #[cfg(feature = "lt2015_1")]
1580 let expected = vec!["sync", "-f", "-k"];
1581
1582 assert_eq!(args_of(&sync.setup_command("p4")), expected);
1583 }
1584
1585 #[test]
1586 fn regular_mode_combined() {
1587 let sync = Sync::new("p4", GlobalOpts::new())
1588 .quiet_mode(true)
1589 .force(true)
1590 .limit(10);
1591
1592 assert_eq!(
1593 args_of(&sync.setup_command("p4")),
1594 ["sync", "-q", "-f", "-m", "10"]
1595 );
1596 }
1597
1598 #[test]
1599 fn regular_mode_preview_result() {
1600 let sync = Sync::new("p4", GlobalOpts::new()).preview_result();
1601
1602 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-n"]);
1603 }
1604
1605 #[test]
1606 fn regular_mode_preview_network_traffic() {
1607 let sync = Sync::new("p4", GlobalOpts::new()).preview_network_traffic();
1608
1609 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-N"]);
1610 }
1611
1612 #[test]
1613 fn regular_mode_preview_with_options() {
1614 let sync = Sync::new("p4", GlobalOpts::new())
1615 .quiet_mode(true)
1616 .preview_result()
1617 .limit(3);
1618
1619 assert_eq!(
1620 args_of(&sync.setup_command("p4")),
1621 ["sync", "-q", "-n", "-m", "3"]
1622 );
1623 }
1624
1625 #[test]
1626 fn regular_mode_parallel() {
1627 let sync = Sync::new("p4", GlobalOpts::new()).parallel(ParallelConfig {
1628 threads: 4,
1629 batch_files: Some(8),
1630 batch_size_bytes: None,
1631 min_files: Some(9),
1632 min_size_bytes: None,
1633 });
1634
1635 assert_eq!(
1636 args_of(&sync.setup_command("p4")),
1637 ["sync", "--parallel=threads=4,batch=8,min=9"]
1638 );
1639 }
1640
1641 #[test]
1642 #[cfg(not(feature = "lt2022_2"))]
1643 fn regular_mode_stream_spec_auto() {
1644 let sync = Sync::new("p4", GlobalOpts::new())
1645 .stream_spec_version(StreamSpecVersion::MaxInFilelists);
1646
1647 assert_eq!(
1648 args_of(&sync.setup_command("p4")),
1649 ["sync", "--use-stream-change"]
1650 );
1651 }
1652
1653 #[test]
1654 #[cfg(not(feature = "lt2022_2"))]
1655 fn regular_mode_stream_spec_current() {
1656 let sync =
1657 Sync::new("p4", GlobalOpts::new()).stream_spec_version(StreamSpecVersion::Current);
1658
1659 assert_eq!(
1660 args_of(&sync.setup_command("p4")),
1661 ["sync", "--use-stream-change=0"]
1662 );
1663 }
1664
1665 #[test]
1666 #[cfg(not(feature = "lt2022_2"))]
1667 fn regular_mode_stream_spec_specific() {
1668 let sync = Sync::new("p4", GlobalOpts::new())
1669 .stream_spec_version(StreamSpecVersion::ChangeNumber(123));
1670
1671 assert_eq!(
1672 args_of(&sync.setup_command("p4")),
1673 ["sync", "--use-stream-change=123"]
1674 );
1675 }
1676
1677 #[test]
1678 fn safe_check_mode() {
1679 let sync = Sync::new("p4", GlobalOpts::new()).enable_safe_check();
1680
1681 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-s"]);
1682 }
1683
1684 #[test]
1685 fn safe_check_mode_with_options() {
1686 let sync = Sync::new("p4", GlobalOpts::new())
1687 .enable_safe_check()
1688 .quiet_mode(true)
1689 .limit(5);
1690
1691 assert_eq!(
1692 args_of(&sync.setup_command("p4")),
1693 ["sync", "-q", "-s", "-m", "5"]
1694 );
1695 }
1696
1697 #[test]
1698 fn populate_mode() {
1699 let sync = Sync::new("p4", GlobalOpts::new()).populate_client_workspace();
1700
1701 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-p"]);
1702 }
1703
1704 #[test]
1705 fn populate_mode_with_options() {
1706 let sync = Sync::new("p4", GlobalOpts::new())
1707 .populate_client_workspace()
1708 .quiet_mode(true)
1709 .limit(5);
1710
1711 assert_eq!(
1712 args_of(&sync.setup_command("p4")),
1713 ["sync", "-q", "-p", "-m", "5"]
1714 );
1715 }
1716
1717 #[test]
1718 fn transition_to_safe_check_from_regular() {
1719 let sync = Sync::new("p4", GlobalOpts::new())
1720 .quiet_mode(true)
1721 .limit(5)
1722 .safe_check();
1723
1724 assert_eq!(
1725 args_of(&sync.setup_command("p4")),
1726 ["sync", "-q", "-s", "-m", "5"]
1727 );
1728 }
1729
1730 #[test]
1731 fn transition_to_populate_from_regular() {
1732 let sync = Sync::new("p4", GlobalOpts::new())
1733 .quiet_mode(true)
1734 .limit(5)
1735 .populate();
1736
1737 assert_eq!(
1738 args_of(&sync.setup_command("p4")),
1739 ["sync", "-q", "-p", "-m", "5"]
1740 );
1741 }
1742
1743 #[test]
1744 fn transition_preserves_preview() {
1745 let sync = Sync::new("p4", GlobalOpts::new())
1746 .preview_result()
1747 .quiet_mode(true)
1748 .safe_check();
1749
1750 assert_eq!(
1751 args_of(&sync.setup_command("p4")),
1752 ["sync", "-q", "-s", "-n"]
1753 );
1754 }
1755
1756 #[test]
1757 fn force_mode_blocks_safe_check() {
1758 let sync = Sync::new("p4", GlobalOpts::new())
1761 .force(true)
1762 .quiet_mode(true);
1763
1764 assert_eq!(args_of(&sync.setup_command("p4")), ["sync", "-q", "-f"]);
1765 }
1766
1767 #[test]
1768 fn all_regular_options_order() {
1769 let sync = Sync::new("p4", GlobalOpts::new())
1770 .script_list_mode(true)
1771 .quiet_mode(true)
1772 .force(true)
1773 .metadata_only(true)
1774 .limit(5)
1775 .parallel(ParallelConfig {
1776 threads: 2,
1777 batch_files: None,
1778 batch_size_bytes: None,
1779 min_files: None,
1780 min_size_bytes: None,
1781 });
1782
1783 #[cfg(not(feature = "lt2022_2"))]
1784 let sync = sync.verify_edge_replication(true);
1785 #[cfg(not(feature = "lt2022_1"))]
1786 let sync = sync.suppress_keyword_expansion(true);
1787 #[cfg(not(feature = "lt2015_1"))]
1788 let sync = sync.reopen_moved_files(true);
1789 #[cfg(not(feature = "lt2022_2"))]
1790 let sync = sync.stream_spec_version(StreamSpecVersion::Current);
1791
1792 let mut expected: Vec<&str> = vec!["sync", "-L", "-q", "-f", "-k"];
1793 #[cfg(not(feature = "lt2022_2"))]
1794 expected.insert(1, "-E");
1795 #[cfg(not(feature = "lt2022_1"))]
1796 {
1797 let k = expected.iter().position(|&a| a == "-L").unwrap() + 1;
1798 expected.insert(k, "-K");
1799 }
1800 #[cfg(not(feature = "lt2015_1"))]
1801 expected.push("-r");
1802 expected.extend(["-m", "5", "--parallel=threads=2"]);
1803 #[cfg(not(feature = "lt2022_2"))]
1804 expected.push("--use-stream-change=0");
1805
1806 assert_eq!(args_of(&sync.setup_command("p4")), expected);
1807 }
1808}