1use std::{
2 ffi::OsStr,
3 path::PathBuf,
4 process::{Child, Command, Stdio},
5};
6
7use super::{DiffOptions, ExclusiveOption, SubCommand, Unselected};
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct BranchMode {
18 branch: String,
19}
20
21impl ExclusiveOption for BranchMode {
22 fn inject_args(&self, command: &mut Command) {
23 command.arg("-b").arg(&self.branch);
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct StreamMode {
33 stream: String,
34
35 parent: Option<String>,
36}
37
38impl ExclusiveOption for StreamMode {
39 fn inject_args(&self, command: &mut Command) {
40 command.arg("-S").arg(&self.stream);
41
42 if let Some(parent) = &self.parent {
43 command.arg("-P").arg(parent);
44 }
45 }
46}
47
48#[cfg(not(feature = "lt2019_1"))]
56#[derive(Debug, Clone, Default, PartialEq, Eq)]
57pub struct StreamSpecMode;
58
59#[cfg(not(feature = "lt2019_1"))]
60impl ExclusiveOption for StreamSpecMode {
61 fn inject_args(&self, command: &mut Command) {
62 command.arg("-As");
63 }
64}
65
66#[derive(Debug, Clone, Default)]
80pub struct DepotContent<M = Unselected> {
81 differing_only: bool,
82
83 quiet_mode: bool,
84
85 diff_nontext: bool,
86
87 unified_patch: bool,
88
89 mode: M,
90}
91
92impl<M: ExclusiveOption> ExclusiveOption for DepotContent<M> {
93 fn inject_args(&self, command: &mut Command) {
94 if self.differing_only {
95 command.arg("-Od");
96 }
97
98 if self.quiet_mode {
99 command.arg("-q");
100 }
101
102 if self.diff_nontext {
103 command.arg("-t");
104 }
105
106 if self.unified_patch {
107 command.arg("-u");
108 }
109
110 self.mode.inject_args(command);
111 }
112}
113
114#[cfg_attr(
115 feature = "lt2014_2",
116 doc = "`p4 [g-opts] diff2 [-dflags -Od -q -t -u] file1[rev] file2[rev]`",
117 doc = "",
118 doc = "`p4 [g-opts] diff2 [-dflags -Od -q -t -u] -b branch [[fromfile[rev]] tofile[rev]]`",
119 doc = "",
120 doc = "`p4 [g-opts] diff2 [-dflags -Od -q -t -u] -S stream [-P parent] [[fromfile[rev]] tofile[rev]]`"
121)]
122#[cfg_attr(
123 all(feature = "lt2015_1", not(feature = "lt2014_2")),
124 doc = "`p4 [g-opts] diff2 [-doptions -Od -q -t -u] file1[rev] file2[rev]`",
125 doc = "",
126 doc = "`p4 [g-opts] diff2 [-doptions -Od -q -t -u] -b branch [[fromfile[rev]] tofile[rev]]`",
127 doc = "",
128 doc = "`p4 [g-opts] diff2 [-doptions -Od -q -t -u] -S stream [-P parent] [[fromfile[rev]] tofile[rev]]`"
129)]
130#[cfg_attr(
131 all(feature = "lt2019_1", not(feature = "lt2015_1")),
132 doc = "`p4 [g-opts] diff2 [-doptions] [-Od -q -t -u] file1[rev] file2[rev]`",
133 doc = "",
134 doc = "`p4 [g-opts] diff2 [-doptions] [-Od -q -t -u] -b branch [[fromfile[rev]] tofile[rev]]`",
135 doc = "",
136 doc = "`p4 [g-opts] diff2 [-doptions] [-Od -q -t -u] [-S stream] [-P parent] [[fromfile[rev]] tofile[rev]]`"
137)]
138#[cfg_attr(
139 not(feature = "lt2019_1"),
140 doc = "`p4 [g-opts] diff2 [-doptions] [-Od -q -t -u] file1[rev] file2[rev]`",
141 doc = "",
142 doc = "`p4 [g-opts] diff2 [-doptions] [-Od -q -t -u] -b branch [[fromfile[rev]] tofile[rev]]`",
143 doc = "",
144 doc = "`p4 [g-opts] diff2 [-doptions] [-Od -q -t -u] [-S stream] [-P parent] [[fromfile[rev]] tofile[rev]]`",
145 doc = "",
146 doc = "`p4 [g-opts] diff2 [-doptions] -As streamname1[@change1] streamname2[@change2]`"
147)]
148#[cfg_attr(
153 not(feature = "lt2019_1"),
154 doc = "Also compares two arbitrary stream specs with the -As option."
155)]
156#[cfg_attr(
158 feature = "lt2019_1",
159 doc = "The `M` type parameter tracks the command mode at compile time. The default [`Unselected`] state diffs the two depot paths given as spawn arguments; [`Self::differing_only`], [`Self::quiet_mode`], [`Self::diff_nontext`], and [`Self::unified_patch`] transition to the [`DepotContent`] state, and [`Self::branch`] and [`Self::stream`] transition to the depot content [`BranchMode`] and [`StreamMode`] sub-modes."
160)]
161#[cfg_attr(
162 not(feature = "lt2019_1"),
163 doc = "The `M` type parameter tracks the command mode at compile time. The default [`Unselected`] state diffs the two depot paths given as spawn arguments; [`Self::differing_only`], [`Self::quiet_mode`], [`Self::diff_nontext`], and [`Self::unified_patch`] transition to the [`DepotContent`] state, [`Self::branch`] and [`Self::stream`] transition to the depot content [`BranchMode`] and [`StreamMode`] sub-modes, and [`Self::stream_spec_mode`] transitions to the [`StreamSpecMode`] state."
164)]
165#[derive(Debug, Clone, Default)]
166pub struct Diff2<M = Unselected> {
167 bin: PathBuf,
168
169 global_opts: GlobalOpts,
170
171 diff_opts: Option<DiffOptions>,
172
173 mode: M,
174}
175
176impl Diff2<Unselected> {
177 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
181 Self {
182 bin: bin.into(),
183 global_opts,
184 diff_opts: None,
185 mode: Unselected,
186 }
187 }
188
189 pub fn differing_only(self, v: bool) -> Diff2<DepotContent<Unselected>> {
198 Diff2 {
199 bin: self.bin,
200 global_opts: self.global_opts,
201 diff_opts: self.diff_opts,
202 mode: DepotContent {
203 differing_only: v,
204 quiet_mode: false,
205 diff_nontext: false,
206 unified_patch: false,
207 mode: Unselected,
208 },
209 }
210 }
211
212 pub fn quiet_mode(self, v: bool) -> Diff2<DepotContent<Unselected>> {
222 Diff2 {
223 bin: self.bin,
224 global_opts: self.global_opts,
225 diff_opts: self.diff_opts,
226 mode: DepotContent {
227 differing_only: false,
228 quiet_mode: v,
229 diff_nontext: false,
230 unified_patch: false,
231 mode: Unselected,
232 },
233 }
234 }
235
236 pub fn diff_nontext(self, v: bool) -> Diff2<DepotContent<Unselected>> {
245 Diff2 {
246 bin: self.bin,
247 global_opts: self.global_opts,
248 diff_opts: self.diff_opts,
249 mode: DepotContent {
250 differing_only: false,
251 quiet_mode: false,
252 diff_nontext: v,
253 unified_patch: false,
254 mode: Unselected,
255 },
256 }
257 }
258
259 #[cfg_attr(feature = "lt2017_2", doc = "remain in Perforce syntax.")]
267 #[cfg_attr(
268 all(feature = "lt2024_1", not(feature = "lt2017_2")),
269 doc = "remain in Helix Server syntax."
270 )]
271 #[cfg_attr(
272 all(feature = "lt2024_2", not(feature = "lt2024_1")),
273 doc = "remain in Helix Core Server syntax."
274 )]
275 #[cfg_attr(not(feature = "lt2024_2"), doc = "remain in P4 Server syntax.")]
276 pub fn unified_patch(self, v: bool) -> Diff2<DepotContent<Unselected>> {
280 Diff2 {
281 bin: self.bin,
282 global_opts: self.global_opts,
283 diff_opts: self.diff_opts,
284 mode: DepotContent {
285 differing_only: false,
286 quiet_mode: false,
287 diff_nontext: false,
288 unified_patch: v,
289 mode: Unselected,
290 },
291 }
292 }
293
294 pub fn branch(self, name: impl Into<String>) -> Diff2<DepotContent<BranchMode>> {
306 Diff2 {
307 bin: self.bin,
308 global_opts: self.global_opts,
309 diff_opts: self.diff_opts,
310 mode: DepotContent {
311 differing_only: false,
312 quiet_mode: false,
313 diff_nontext: false,
314 unified_patch: false,
315 mode: BranchMode {
316 branch: name.into(),
317 },
318 },
319 }
320 }
321
322 pub fn stream(self, name: impl Into<String>) -> Diff2<DepotContent<StreamMode>> {
333 Diff2 {
334 bin: self.bin,
335 global_opts: self.global_opts,
336 diff_opts: self.diff_opts,
337 mode: DepotContent {
338 differing_only: false,
339 quiet_mode: false,
340 diff_nontext: false,
341 unified_patch: false,
342 mode: StreamMode {
343 stream: name.into(),
344 parent: None,
345 },
346 },
347 }
348 }
349
350 #[cfg(not(feature = "lt2019_1"))]
369 pub fn stream_spec_mode(self) -> Diff2<StreamSpecMode> {
370 Diff2 {
371 bin: self.bin,
372 global_opts: self.global_opts,
373 diff_opts: self.diff_opts,
374 mode: StreamSpecMode,
375 }
376 }
377}
378
379impl<M> Diff2<M> {
380 #[cfg_attr(
385 feature = "lt2014_2",
386 doc = "Runs the diff routine with one of a subset of the standard UNIX diff flags. See the Usage Notes below for a listing of these flags."
387 )]
388 #[cfg_attr(
389 all(feature = "lt2015_1", not(feature = "lt2014_2")),
390 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See the Usage Notes below for a listing of these options."
391 )]
392 #[cfg_attr(
393 all(feature = "lt2024_1", not(feature = "lt2015_1")),
394 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See Usage Notes for a listing of these options."
395 )]
396 #[cfg_attr(
397 not(feature = "lt2024_1"),
398 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See Usage notes for a listing of these options."
399 )]
400 pub fn get_diff_options(&self) -> Option<&DiffOptions> {
401 self.diff_opts.as_ref()
402 }
403
404 #[cfg_attr(
409 feature = "lt2014_2",
410 doc = "Runs the diff routine with one of a subset of the standard UNIX diff flags. See the Usage Notes below for a listing of these flags."
411 )]
412 #[cfg_attr(
413 all(feature = "lt2015_1", not(feature = "lt2014_2")),
414 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See the Usage Notes below for a listing of these options."
415 )]
416 #[cfg_attr(
417 all(feature = "lt2024_1", not(feature = "lt2015_1")),
418 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See Usage Notes for a listing of these options."
419 )]
420 #[cfg_attr(
421 not(feature = "lt2024_1"),
422 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See Usage notes for a listing of these options."
423 )]
424 pub fn set_diff_options(&mut self, v: impl Into<DiffOptions>) -> &mut Self {
425 self.diff_opts = Some(v.into());
426 self
427 }
428
429 #[cfg_attr(
434 feature = "lt2014_2",
435 doc = "Runs the diff routine with one of a subset of the standard UNIX diff flags. See the Usage Notes below for a listing of these flags."
436 )]
437 #[cfg_attr(
438 all(feature = "lt2015_1", not(feature = "lt2014_2")),
439 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See the Usage Notes below for a listing of these options."
440 )]
441 #[cfg_attr(
442 all(feature = "lt2024_1", not(feature = "lt2015_1")),
443 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See Usage Notes for a listing of these options."
444 )]
445 #[cfg_attr(
446 not(feature = "lt2024_1"),
447 doc = "Runs the diff routine with one of a subset of the standard UNIX diff options. See Usage notes for a listing of these options."
448 )]
449 pub fn diff_options(mut self, v: impl Into<DiffOptions>) -> Self {
450 self.diff_opts = Some(v.into());
451 self
452 }
453}
454
455impl<M: ExclusiveOption> Diff2<M> {
456 #[cfg_attr(
461 feature = "lt2014_2",
462 doc = "See the [Global Options](GlobalOpts) section."
463 )]
464 #[cfg_attr(
465 all(feature = "lt2015_1", not(feature = "lt2014_2")),
466 doc = "See the [“Global Options”](GlobalOpts) section."
467 )]
468 #[cfg_attr(
469 all(feature = "lt2017_1", not(feature = "lt2015_1")),
470 doc = "See [“Global Options”](GlobalOpts)."
471 )]
472 #[cfg_attr(
473 all(feature = "lt2018_2", not(feature = "lt2017_1")),
474 doc = "See [Global Options](GlobalOpts)."
475 )]
476 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
477 pub fn get_global_opts(&self) -> &GlobalOpts {
478 &self.global_opts
479 }
480
481 #[cfg_attr(
486 feature = "lt2014_2",
487 doc = "See the [Global Options](GlobalOpts) section."
488 )]
489 #[cfg_attr(
490 all(feature = "lt2015_1", not(feature = "lt2014_2")),
491 doc = "See the [“Global Options”](GlobalOpts) section."
492 )]
493 #[cfg_attr(
494 all(feature = "lt2017_1", not(feature = "lt2015_1")),
495 doc = "See [“Global Options”](GlobalOpts)."
496 )]
497 #[cfg_attr(
498 all(feature = "lt2018_2", not(feature = "lt2017_1")),
499 doc = "See [Global Options](GlobalOpts)."
500 )]
501 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
502 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
503 self.global_opts = v;
504 self
505 }
506
507 #[cfg_attr(
512 feature = "lt2014_2",
513 doc = "See the [Global Options](GlobalOpts) section."
514 )]
515 #[cfg_attr(
516 all(feature = "lt2015_1", not(feature = "lt2014_2")),
517 doc = "See the [“Global Options”](GlobalOpts) section."
518 )]
519 #[cfg_attr(
520 all(feature = "lt2017_1", not(feature = "lt2015_1")),
521 doc = "See [“Global Options”](GlobalOpts)."
522 )]
523 #[cfg_attr(
524 all(feature = "lt2018_2", not(feature = "lt2017_1")),
525 doc = "See [Global Options](GlobalOpts)."
526 )]
527 #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
528 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
529 self.global_opts = v;
530 self
531 }
532}
533
534impl<M> Diff2<DepotContent<M>> {
535 pub fn get_differing_only(&self) -> bool {
541 self.mode.differing_only
542 }
543
544 pub fn set_differing_only(&mut self, v: bool) -> &mut Self {
550 self.mode.differing_only = v;
551 self
552 }
553
554 pub fn differing_only(mut self, v: bool) -> Self {
560 self.mode.differing_only = v;
561 self
562 }
563
564 pub fn get_quiet_mode(&self) -> bool {
571 self.mode.quiet_mode
572 }
573
574 pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
581 self.mode.quiet_mode = v;
582 self
583 }
584
585 pub fn quiet_mode(mut self, v: bool) -> Self {
592 self.mode.quiet_mode = v;
593 self
594 }
595
596 pub fn get_diff_nontext(&self) -> bool {
602 self.mode.diff_nontext
603 }
604
605 pub fn set_diff_nontext(&mut self, v: bool) -> &mut Self {
611 self.mode.diff_nontext = v;
612 self
613 }
614
615 pub fn diff_nontext(mut self, v: bool) -> Self {
621 self.mode.diff_nontext = v;
622 self
623 }
624
625 #[cfg_attr(feature = "lt2017_2", doc = "remain in Perforce syntax.")]
633 #[cfg_attr(
634 all(feature = "lt2024_1", not(feature = "lt2017_2")),
635 doc = "remain in Helix Server syntax."
636 )]
637 #[cfg_attr(
638 all(feature = "lt2024_2", not(feature = "lt2024_1")),
639 doc = "remain in Helix Core Server syntax."
640 )]
641 #[cfg_attr(not(feature = "lt2024_2"), doc = "remain in P4 Server syntax.")]
642 pub fn get_unified_patch(&self) -> bool {
643 self.mode.unified_patch
644 }
645
646 #[cfg_attr(feature = "lt2017_2", doc = "remain in Perforce syntax.")]
654 #[cfg_attr(
655 all(feature = "lt2024_1", not(feature = "lt2017_2")),
656 doc = "remain in Helix Server syntax."
657 )]
658 #[cfg_attr(
659 all(feature = "lt2024_2", not(feature = "lt2024_1")),
660 doc = "remain in Helix Core Server syntax."
661 )]
662 #[cfg_attr(not(feature = "lt2024_2"), doc = "remain in P4 Server syntax.")]
663 pub fn set_unified_patch(&mut self, v: bool) -> &mut Self {
664 self.mode.unified_patch = v;
665 self
666 }
667
668 #[cfg_attr(feature = "lt2017_2", doc = "remain in Perforce syntax.")]
676 #[cfg_attr(
677 all(feature = "lt2024_1", not(feature = "lt2017_2")),
678 doc = "remain in Helix Server syntax."
679 )]
680 #[cfg_attr(
681 all(feature = "lt2024_2", not(feature = "lt2024_1")),
682 doc = "remain in Helix Core Server syntax."
683 )]
684 #[cfg_attr(not(feature = "lt2024_2"), doc = "remain in P4 Server syntax.")]
685 pub fn unified_patch(mut self, v: bool) -> Self {
686 self.mode.unified_patch = v;
687 self
688 }
689}
690
691impl Diff2<DepotContent<Unselected>> {
692 pub fn branch(self, name: impl Into<String>) -> Diff2<DepotContent<BranchMode>> {
704 Diff2 {
705 bin: self.bin,
706 global_opts: self.global_opts,
707 diff_opts: self.diff_opts,
708 mode: DepotContent {
709 differing_only: self.mode.differing_only,
710 quiet_mode: self.mode.quiet_mode,
711 diff_nontext: self.mode.diff_nontext,
712 unified_patch: self.mode.unified_patch,
713 mode: BranchMode {
714 branch: name.into(),
715 },
716 },
717 }
718 }
719
720 pub fn stream(self, name: impl Into<String>) -> Diff2<DepotContent<StreamMode>> {
731 Diff2 {
732 bin: self.bin,
733 global_opts: self.global_opts,
734 diff_opts: self.diff_opts,
735 mode: DepotContent {
736 differing_only: self.mode.differing_only,
737 quiet_mode: self.mode.quiet_mode,
738 diff_nontext: self.mode.diff_nontext,
739 unified_patch: self.mode.unified_patch,
740 mode: StreamMode {
741 stream: name.into(),
742 parent: None,
743 },
744 },
745 }
746 }
747}
748
749impl Diff2<DepotContent<BranchMode>> {
750 pub fn get_branch(&self) -> &str {
758 &self.mode.mode.branch
759 }
760
761 pub fn set_branch(&mut self, v: impl Into<String>) -> &mut Self {
769 self.mode.mode.branch = v.into();
770 self
771 }
772
773 pub fn branch(mut self, v: impl Into<String>) -> Self {
781 self.mode.mode.branch = v.into();
782 self
783 }
784}
785
786impl Diff2<DepotContent<StreamMode>> {
787 pub fn get_stream(&self) -> &str {
795 &self.mode.mode.stream
796 }
797
798 pub fn set_stream(&mut self, v: impl Into<String>) -> &mut Self {
806 self.mode.mode.stream = v.into();
807 self
808 }
809
810 pub fn stream(mut self, v: impl Into<String>) -> Self {
818 self.mode.mode.stream = v.into();
819 self
820 }
821
822 pub fn get_parent(&self) -> Option<&str> {
828 self.mode.mode.parent.as_deref()
829 }
830
831 pub fn set_parent(&mut self, v: impl Into<String>) -> &mut Self {
837 self.mode.mode.parent = Some(v.into());
838 self
839 }
840
841 pub fn parent(mut self, v: impl Into<String>) -> Self {
847 self.mode.mode.parent = Some(v.into());
848 self
849 }
850}
851
852impl<M: ExclusiveOption> SubCommand for Diff2<M> {
853 fn name(&self) -> &str {
854 "diff2"
855 }
856
857 fn inject_local_args(&self, command: &mut Command) {
858 if let Some(diff_opts) = &self.diff_opts {
859 diff_opts.inject_arg(command);
860 }
861
862 self.mode.inject_args(command);
863 }
864
865 fn global_opts(&self) -> Option<&GlobalOpts> {
866 Some(&self.global_opts)
867 }
868}
869
870impl<A, B> ParameterizedSpawn<(A, B)> for Diff2<Unselected>
871where
872 A: AsRef<OsStr>,
873 B: AsRef<OsStr>,
874{
875 type Output = Child;
876 type Error = std::io::Error;
877
878 fn spawn_with(&mut self, (file1, file2): (A, B)) -> Result<Self::Output, Self::Error> {
885 self.setup_command(&self.bin)
886 .arg(file1)
887 .arg(file2)
888 .stdout(Stdio::piped())
889 .stderr(Stdio::piped())
890 .spawn()
891 }
892}
893
894impl<A, B> ParameterizedSpawn<(A, B)> for Diff2<DepotContent<Unselected>>
895where
896 A: AsRef<OsStr>,
897 B: AsRef<OsStr>,
898{
899 type Output = Child;
900 type Error = std::io::Error;
901
902 fn spawn_with(&mut self, (file1, file2): (A, B)) -> Result<Self::Output, Self::Error> {
909 self.setup_command(&self.bin)
910 .arg(file1)
911 .arg(file2)
912 .stdout(Stdio::piped())
913 .stderr(Stdio::piped())
914 .spawn()
915 }
916}
917
918impl ParameterizedSpawn<()> for Diff2<DepotContent<BranchMode>> {
919 type Output = Child;
920 type Error = std::io::Error;
921
922 fn spawn_with(&mut self, _: ()) -> Result<Self::Output, Self::Error> {
927 self.setup_command(&self.bin)
928 .stdout(Stdio::piped())
929 .stderr(Stdio::piped())
930 .spawn()
931 }
932}
933
934impl<I> ParameterizedSpawn<(I,)> for Diff2<DepotContent<BranchMode>>
935where
936 I: AsRef<OsStr>,
937{
938 type Output = Child;
939 type Error = std::io::Error;
940
941 fn spawn_with(&mut self, (tofile,): (I,)) -> Result<Self::Output, Self::Error> {
948 self.setup_command(&self.bin)
949 .arg(tofile)
950 .stdout(Stdio::piped())
951 .stderr(Stdio::piped())
952 .spawn()
953 }
954}
955
956impl<A, B> ParameterizedSpawn<(A, B)> for Diff2<DepotContent<BranchMode>>
957where
958 A: AsRef<OsStr>,
959 B: AsRef<OsStr>,
960{
961 type Output = Child;
962 type Error = std::io::Error;
963
964 fn spawn_with(&mut self, (fromfile, tofile): (A, B)) -> Result<Self::Output, Self::Error> {
969 self.setup_command(&self.bin)
970 .arg(fromfile)
971 .arg(tofile)
972 .stdout(Stdio::piped())
973 .stderr(Stdio::piped())
974 .spawn()
975 }
976}
977
978impl ParameterizedSpawn<()> for Diff2<DepotContent<StreamMode>> {
979 type Output = Child;
980 type Error = std::io::Error;
981
982 fn spawn_with(&mut self, _: ()) -> Result<Self::Output, Self::Error> {
987 self.setup_command(&self.bin)
988 .stdout(Stdio::piped())
989 .stderr(Stdio::piped())
990 .spawn()
991 }
992}
993
994impl<I> ParameterizedSpawn<(I,)> for Diff2<DepotContent<StreamMode>>
995where
996 I: AsRef<OsStr>,
997{
998 type Output = Child;
999 type Error = std::io::Error;
1000
1001 fn spawn_with(&mut self, (tofile,): (I,)) -> Result<Self::Output, Self::Error> {
1008 self.setup_command(&self.bin)
1009 .arg(tofile)
1010 .stdout(Stdio::piped())
1011 .stderr(Stdio::piped())
1012 .spawn()
1013 }
1014}
1015
1016impl<A, B> ParameterizedSpawn<(A, B)> for Diff2<DepotContent<StreamMode>>
1017where
1018 A: AsRef<OsStr>,
1019 B: AsRef<OsStr>,
1020{
1021 type Output = Child;
1022 type Error = std::io::Error;
1023
1024 fn spawn_with(&mut self, (fromfile, tofile): (A, B)) -> Result<Self::Output, Self::Error> {
1029 self.setup_command(&self.bin)
1030 .arg(fromfile)
1031 .arg(tofile)
1032 .stdout(Stdio::piped())
1033 .stderr(Stdio::piped())
1034 .spawn()
1035 }
1036}
1037
1038#[cfg(not(feature = "lt2019_1"))]
1039impl<A, B> ParameterizedSpawn<(A, B)> for Diff2<StreamSpecMode>
1040where
1041 A: AsRef<OsStr>,
1042 B: AsRef<OsStr>,
1043{
1044 type Output = Child;
1045 type Error = std::io::Error;
1046
1047 fn spawn_with(&mut self, (spec1, spec2): (A, B)) -> Result<Self::Output, Self::Error> {
1056 self.setup_command(&self.bin)
1057 .arg(spec1)
1058 .arg(spec2)
1059 .stdout(Stdio::piped())
1060 .stderr(Stdio::piped())
1061 .spawn()
1062 }
1063}
1064
1065#[cfg(test)]
1066mod tests {
1067 use super::*;
1068 use crate::cmd::DiffOptionsBuilder;
1069 use crate::cmd::args_of;
1070
1071 #[test]
1074 fn without_options() {
1075 let diff2 = Diff2::new("p4", GlobalOpts::new());
1076
1077 assert_eq!(args_of(&diff2.setup_command("p4")), ["diff2"]);
1078 }
1079
1080 #[test]
1081 fn depot_content_flags_are_injected() {
1082 let diff2 = Diff2::new("p4", GlobalOpts::new())
1083 .differing_only(true)
1084 .quiet_mode(true)
1085 .diff_nontext(true)
1086 .unified_patch(true);
1087
1088 assert_eq!(
1089 args_of(&diff2.setup_command("p4")),
1090 ["diff2", "-Od", "-q", "-t", "-u"]
1091 );
1092 }
1093
1094 #[test]
1095 fn depot_content_flags_apply_to_branch_mode() {
1096 let diff2 = Diff2::new("p4", GlobalOpts::new())
1097 .quiet_mode(true)
1098 .branch("branch2");
1099
1100 assert!(diff2.get_quiet_mode());
1101 assert_eq!(
1102 args_of(&diff2.setup_command("p4")),
1103 ["diff2", "-q", "-b", "branch2"]
1104 );
1105 }
1106
1107 #[test]
1108 fn depot_content_flags_apply_after_branch_transition() {
1109 let diff2 = Diff2::new("p4", GlobalOpts::new())
1110 .branch("branch2")
1111 .quiet_mode(true);
1112
1113 assert!(diff2.get_quiet_mode());
1114 assert_eq!(
1115 args_of(&diff2.setup_command("p4")),
1116 ["diff2", "-q", "-b", "branch2"]
1117 );
1118 }
1119
1120 #[test]
1121 fn diff_options_are_injected() {
1122 let mut diff2 =
1123 Diff2::new("p4", GlobalOpts::new()).diff_options(DiffOptionsBuilder::unified(None));
1124 diff2.set_diff_options(DiffOptionsBuilder::summary());
1125
1126 assert_eq!(args_of(&diff2.setup_command("p4")), ["diff2", "-ds"]);
1127 }
1128
1129 #[test]
1130 fn branch_mode_injects_branch() {
1131 let diff2 = Diff2::new("p4", GlobalOpts::new()).branch("branch2");
1132
1133 assert_eq!(diff2.get_branch(), "branch2");
1134 assert_eq!(
1135 args_of(&diff2.setup_command("p4")),
1136 ["diff2", "-b", "branch2"]
1137 );
1138 }
1139
1140 #[test]
1141 fn branch_mode_with_files() {
1142 let diff2 = Diff2::new("p4", GlobalOpts::new()).branch("branch2");
1143
1144 let mut command = diff2.setup_command("p4");
1147 command.arg(OsStr::new("//depot/rel1/..."));
1148 command.arg(OsStr::new("//depot/rel2/...#4"));
1149
1150 assert_eq!(
1151 args_of(&command),
1152 [
1153 "diff2",
1154 "-b",
1155 "branch2",
1156 "//depot/rel1/...",
1157 "//depot/rel2/...#4"
1158 ]
1159 );
1160 }
1161
1162 #[test]
1163 fn branch_mode_with_tofile() {
1164 let diff2 = Diff2::new("p4", GlobalOpts::new()).branch("branch2");
1165
1166 let mut command = diff2.setup_command("p4");
1169 command.arg(OsStr::new("//depot/rel2/...#4"));
1170
1171 assert_eq!(
1172 args_of(&command),
1173 ["diff2", "-b", "branch2", "//depot/rel2/...#4"]
1174 );
1175 }
1176
1177 #[test]
1178 fn stream_mode_injects_stream() {
1179 let diff2 = Diff2::new("p4", GlobalOpts::new()).stream("myStream");
1180
1181 assert_eq!(diff2.get_stream(), "myStream");
1182 assert_eq!(
1183 args_of(&diff2.setup_command("p4")),
1184 ["diff2", "-S", "myStream"]
1185 );
1186 }
1187
1188 #[test]
1189 fn stream_mode_with_parent() {
1190 let mut diff2 = Diff2::new("p4", GlobalOpts::new()).stream("myStream");
1191 diff2.set_parent("mainStream");
1192
1193 assert_eq!(diff2.get_parent(), Some("mainStream"));
1194 assert_eq!(
1195 args_of(&diff2.setup_command("p4")),
1196 ["diff2", "-S", "myStream", "-P", "mainStream"]
1197 );
1198 }
1199
1200 #[test]
1201 fn stream_mode_with_tofile() {
1202 let diff2 = Diff2::new("p4", GlobalOpts::new()).stream("myStream");
1203
1204 let mut command = diff2.setup_command("p4");
1207 command.arg(OsStr::new("//depot/rel2/...#4"));
1208
1209 assert_eq!(
1210 args_of(&command),
1211 ["diff2", "-S", "myStream", "//depot/rel2/...#4"]
1212 );
1213 }
1214
1215 #[test]
1216 fn stream_mode_preserves_diff_options() {
1217 let diff2 = Diff2::new("p4", GlobalOpts::new())
1218 .diff_options(DiffOptionsBuilder::summary())
1219 .stream("myStream");
1220
1221 assert_eq!(
1222 args_of(&diff2.setup_command("p4")),
1223 ["diff2", "-ds", "-S", "myStream"]
1224 );
1225 }
1226
1227 #[cfg(not(feature = "lt2019_1"))]
1228 #[test]
1229 fn stream_spec_mode_bare() {
1230 let diff2 = Diff2::new("p4", GlobalOpts::new()).stream_spec_mode();
1231
1232 assert_eq!(args_of(&diff2.setup_command("p4")), ["diff2", "-As"]);
1233 }
1234
1235 #[cfg(not(feature = "lt2019_1"))]
1236 #[test]
1237 fn stream_spec_mode_with_specs() {
1238 let diff2 = Diff2::new("p4", GlobalOpts::new()).stream_spec_mode();
1239
1240 let mut command = diff2.setup_command("p4");
1243 command.arg("myStream@=1");
1244 command.arg("yourStream@2");
1245
1246 assert_eq!(
1247 args_of(&command),
1248 ["diff2", "-As", "myStream@=1", "yourStream@2"]
1249 );
1250 }
1251}