1use std::{
2 ffi::OsStr,
3 marker::PhantomData,
4 path::{Path, PathBuf},
5 process::{Child, Command, Stdio},
6};
7
8use super::{ExclusiveOption, SubCommand, Unselected};
9
10use crate::global::GlobalOpts;
11use crate::spawn::ParameterizedSpawn;
12
13#[cfg_attr(not(feature = "lt2022_1"), doc = "[`SwitchViewMode`],")]
22#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
24pub struct ForceSet;
25
26impl ExclusiveOption for ForceSet {
27 fn inject_args(&self, command: &mut Command) {
28 command.arg("-f");
29 }
30}
31
32#[derive(Debug, Clone, Default, PartialEq, Eq)]
38pub struct TemplateSet {
39 template: String,
40}
41
42impl ExclusiveOption for TemplateSet {
43 fn inject_args(&self, command: &mut Command) {
44 command.arg("-t").arg(&self.template);
45 }
46}
47
48#[cfg(not(feature = "lt2016_2"))]
54#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
55pub struct TypeSet {
56 workspace_type: WorkSpaceType,
57}
58
59#[cfg(not(feature = "lt2016_2"))]
60impl ExclusiveOption for TypeSet {
61 fn inject_args(&self, command: &mut Command) {
62 command.arg("-T").arg(self.workspace_type.as_str());
63 }
64}
65
66#[cfg(not(feature = "lt2016_2"))]
70#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
71pub enum WorkSpaceType {
72 #[default]
75 Writable,
76 Readonly,
79 Partitioned,
82 #[cfg(not(feature = "lt2024_1"))]
85 PartitionedJnl,
86 #[cfg(not(feature = "lt2024_1"))]
89 Graph,
90}
91
92#[cfg(not(feature = "lt2016_2"))]
93impl WorkSpaceType {
94 fn as_str(&self) -> &'static str {
95 match self {
96 WorkSpaceType::Writable => "writeable",
97 WorkSpaceType::Readonly => "readonly",
98 WorkSpaceType::Partitioned => "partitioned",
99 #[cfg(not(feature = "lt2024_1"))]
100 WorkSpaceType::PartitionedJnl => "partitioned-jnl",
101 #[cfg(not(feature = "lt2024_1"))]
102 WorkSpaceType::Graph => "graph",
103 }
104 }
105}
106
107#[cfg_attr(
112 feature = "lt2016_2",
113 doc = "The default form: `p4 client [-f] [-t template] [name]`.",
114 doc = "",
115 doc = "Create or edit a client workspace specification. This is the initial state of [`Client`]; from here the command can transition into any of the other modes, subject to the options already set:",
116 doc = "",
117 doc = "- with `-f` set, [`Client::delete`] and [`Client::stdin`] remain available (both of them accept `-f`)",
118 doc = "- with `-t` set, [`Client::stdout`] remains available (the `-o` form accepts it)"
119)]
120#[cfg_attr(
121 all(feature = "lt2022_1", not(feature = "lt2016_2")),
122 doc = "The default form: `p4 client [-f] [-t template] [-T type] [name]`.",
123 doc = "",
124 doc = "Create or edit a client workspace specification. This is the initial state of [`Client`]; from here the command can transition into any of the other modes, subject to the options already set:",
125 doc = "",
126 doc = "- with `-f` set, [`Client::delete`] and [`Client::stdin`] remain available (both of them accept `-f`)",
127 doc = "- with `-t` / `-T` set, [`Client::stdout`] remains available (the `-o` form accepts both)"
128)]
129#[cfg_attr(
130 not(feature = "lt2022_1"),
131 doc = "The default form: `p4 client [-f] [-t template] [-T type] [name]`.",
132 doc = "",
133 doc = "Create or edit a client workspace specification. This is the initial state of [`Client`]; from here the command can transition into any of the other modes, subject to the options already set:",
134 doc = "",
135 doc = "- with `-f` set, [`Client::delete`], [`Client::switch_view_stream`], [`Client::switch_view_template`], [`Client::switch_view`], and [`Client::stdin`] remain available (all of them accept `-f`)",
136 doc = "- with `-t` / `-T` set, [`Client::stdout`] remains available (the `-o` form accepts both)"
137)]
138#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
139pub struct GeneralMode;
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum ShelvedHandling {
144 Keep,
147 #[cfg(not(feature = "lt2024_1"))]
149 Delete,
150}
151
152#[cfg(not(feature = "lt2015_2"))]
154#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
155pub struct ServerIdSet;
156
157#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
160pub struct ShelvedKept;
161
162#[cfg(not(feature = "lt2024_1"))]
165#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
166pub struct ShelvedDeleted;
167
168#[doc(hidden)]
170pub trait ServerIdState {
171 const SET: bool;
173}
174
175impl ServerIdState for Unselected {
176 const SET: bool = false;
177}
178
179#[cfg(not(feature = "lt2015_2"))]
180impl ServerIdState for ServerIdSet {
181 const SET: bool = true;
182}
183
184#[doc(hidden)]
186pub trait ShelvedState {
187 fn handling() -> Option<ShelvedHandling>;
189
190 fn inject_shelved(command: &mut Command);
192}
193
194impl ShelvedState for Unselected {
195 fn handling() -> Option<ShelvedHandling> {
196 None
197 }
198
199 fn inject_shelved(_: &mut Command) {}
200}
201
202impl ShelvedState for ShelvedKept {
203 fn handling() -> Option<ShelvedHandling> {
204 Some(ShelvedHandling::Keep)
205 }
206
207 fn inject_shelved(command: &mut Command) {
208 command.arg("-Fs");
209 }
210}
211
212#[cfg(not(feature = "lt2024_1"))]
213impl ShelvedState for ShelvedDeleted {
214 fn handling() -> Option<ShelvedHandling> {
215 Some(ShelvedHandling::Delete)
216 }
217
218 fn inject_shelved(command: &mut Command) {
219 command.arg("-Fd");
220 }
221}
222
223#[doc(hidden)]
227pub trait ServerIdCompatible: ShelvedState {}
228
229impl ServerIdCompatible for Unselected {}
230impl ServerIdCompatible for ShelvedKept {}
231
232#[cfg_attr(
240 not(feature = "lt2024_1"),
241 doc = "- `(Unselected, ShelvedDeleted, ForceSet)` — `p4 client -d -f -Fd name`"
242)]
243#[cfg_attr(
244 not(feature = "lt2015_2"),
245 doc = "- `(ServerIdSet, Unselected, ForceSet)` — `p4 client -d -f --serverid=X name`",
246 doc = "- `(ServerIdSet, ShelvedKept, ForceSet)` — `p4 client -d -f --serverid=X -Fs name`"
247)]
248#[doc(hidden)]
254pub trait ValidDeleteCombination {}
255
256impl ValidDeleteCombination for (Unselected, Unselected, Unselected) {}
257impl ValidDeleteCombination for (Unselected, Unselected, ForceSet) {}
258impl ValidDeleteCombination for (Unselected, ShelvedKept, ForceSet) {}
259#[cfg(not(feature = "lt2024_1"))]
260impl ValidDeleteCombination for (Unselected, ShelvedDeleted, ForceSet) {}
261#[cfg(not(feature = "lt2015_2"))]
262impl ValidDeleteCombination for (ServerIdSet, Unselected, ForceSet) {}
263#[cfg(not(feature = "lt2015_2"))]
264impl ValidDeleteCombination for (ServerIdSet, ShelvedKept, ForceSet) {}
265
266#[cfg_attr(
267 feature = "lt2015_2",
268 doc = "The delete form: `p4 client -d [-f [-Fs]] name`.",
269 doc = "",
270 doc = "The `S` type parameter tracks the shelved handling: [`Unselected`] or [`ShelvedKept`] (`-Fs`).",
271 doc = "",
272 doc = "The options may be selected in any order (for example `keep_shelved().force()` is equivalent to `force().keep_shelved()`); a state where `-Fs` is set without `-f` is an intermediate state that cannot be spawned until [`Client::force`] is called. Entered with [`Client::delete`], or directly from the [`GeneralMode`] state via [`Client::keep_shelved`]."
273)]
274#[cfg_attr(
275 all(feature = "lt2024_1", not(feature = "lt2015_2")),
276 doc = "The delete form: `p4 client -d [-f [-Fs]] name` and `p4 client -d -f --serverid=X [-Fs] name`.",
277 doc = "",
278 doc = "The two type parameters encode the documented compatibility matrix at compile time:",
279 doc = "",
280 doc = "- `I` tracks `--serverid=X`: [`Unselected`] or [`ServerIdSet`]",
281 doc = "- `S` tracks the shelved handling: [`Unselected`] or [`ShelvedKept`] (`-Fs`)",
282 doc = "",
283 doc = "`--serverid=X` can only be combined with `-Fs`; the illegal combinations have no reachable state. The options may be selected in any order (for example `keep_shelved().force()` is equivalent to `force().keep_shelved()`); a state where `-Fs` or `--serverid=X` is set without `-f` is an intermediate state that cannot be spawned until [`Client::force`] is called. Entered with [`Client::delete`], or directly from the [`GeneralMode`] state via [`Client::server_id`] and [`Client::keep_shelved`]."
284)]
285#[cfg_attr(
286 not(feature = "lt2024_1"),
287 doc = "The delete form: `p4 client -d [-f [-Fs|-Fd]] name` and `p4 client -d -f --serverid=X [-Fs] name`.",
288 doc = "",
289 doc = "The two type parameters encode the documented compatibility matrix at compile time:",
290 doc = "",
291 doc = "- `I` tracks `--serverid=X`: [`Unselected`] or [`ServerIdSet`]",
292 doc = "- `S` tracks the shelved handling: [`Unselected`], [`ShelvedKept`] (`-Fs`), or [`ShelvedDeleted`] (`-Fd`)",
293 doc = "",
294 doc = "`-Fs` and `-Fd` are mutually exclusive, and `--serverid=X` can only be combined with `-Fs`, never with `-Fd`; the illegal combinations have no reachable state. The options may be selected in any order (for example `delete_shelved().force()` is equivalent to `force().delete_shelved()`); a state where `-Fs`, `-Fd`, or `--serverid=X` is set without `-f` is an intermediate state that cannot be spawned until [`Client::force`] is called. Entered with [`Client::delete`], or directly from the [`GeneralMode`] state via [`Client::server_id`], [`Client::keep_shelved`], and [`Client::delete_shelved`]."
295)]
296#[derive(Debug, Clone, Default, PartialEq, Eq)]
297pub struct DeleteMode<I = Unselected, S = Unselected> {
298 server_id: Option<String>,
299 serverid_state: PhantomData<I>,
300 shelved: S,
301}
302
303#[cfg_attr(
304 feature = "lt2016_2",
305 doc = "The output form: `p4 client -o [-t template] [name]`."
306)]
307#[cfg_attr(
308 not(feature = "lt2016_2"),
309 doc = "The output form: `p4 client -o [-t template] [-T type] [name]`."
310)]
311#[cfg_attr(
315 feature = "lt2016_2",
316 doc = "carries the `-t` option over from [`GeneralMode`]."
317)]
318#[cfg_attr(
319 not(feature = "lt2016_2"),
320 doc = "carries the `-t` and `-T` options over from [`GeneralMode`]."
321)]
322#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
323pub struct StdoutMode;
324
325#[derive(Debug, Clone, Default, PartialEq, Eq)]
331pub struct StdoutOutput {
332 change: Option<String>,
333}
334
335impl ExclusiveOption for StdoutOutput {
336 fn inject_args(&self, command: &mut Command) {
337 if let Some(change) = &self.change {
338 command.arg("-c").arg(change);
339 }
340 command.arg("-o");
341 }
342}
343
344#[derive(Debug, Clone, Default, PartialEq, Eq)]
351pub struct StreamMode<O = Unselected> {
352 stream: String,
353 output: O,
354}
355
356#[derive(Debug, Clone, Default, PartialEq, Eq)]
361pub struct StreamSwitch {
362 stream: String,
363}
364
365impl ExclusiveOption for StreamSwitch {
366 fn inject_args(&self, command: &mut Command) {
367 command.arg("-S").arg(&self.stream);
368 }
369}
370
371#[derive(Debug, Clone, Default, PartialEq, Eq)]
376pub struct TemplateSwitch {
377 template: String,
378}
379
380impl ExclusiveOption for TemplateSwitch {
381 fn inject_args(&self, command: &mut Command) {
382 command.arg("-t").arg(&self.template);
383 }
384}
385
386#[cfg_attr(
387 feature = "lt2022_1",
388 doc = "The switch-view form: `p4 client -s (-S stream | -t template) [name]`."
389)]
390#[cfg_attr(
391 not(feature = "lt2022_1"),
392 doc = "The switch-view form: `p4 client -s [-f] (-S stream | -t template) [name]`."
393)]
394#[cfg_attr(
397 not(feature = "lt2022_1"),
398 doc = "The `-f` option can be used with `-s` to force switching with opened files."
399)]
400#[derive(Debug, Clone, Default, PartialEq, Eq)]
406pub struct SwitchViewMode<V> {
407 view: V,
408}
409
410#[doc(hidden)]
414pub trait SwitchViewForceCompatible: ExclusiveOption {}
415
416impl SwitchViewForceCompatible for Unselected {}
417
418#[cfg(not(feature = "lt2022_1"))]
419impl SwitchViewForceCompatible for ForceSet {}
420
421#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
426pub struct StdinMode;
427
428pub trait ClientMode<F, T, W> {
435 fn inject_mode_args(&self, command: &mut Command, force: &F, template: &T, workspace_type: &W);
437}
438
439impl<F, T, W> ClientMode<F, T, W> for GeneralMode
440where
441 F: ExclusiveOption,
442 T: ExclusiveOption,
443 W: ExclusiveOption,
444{
445 fn inject_mode_args(&self, command: &mut Command, force: &F, template: &T, workspace_type: &W) {
446 force.inject_args(command);
447 template.inject_args(command);
448 workspace_type.inject_args(command);
449 }
450}
451
452impl<I, S, F> ClientMode<F, Unselected, Unselected> for DeleteMode<I, S>
453where
454 I: ServerIdState,
455 S: ShelvedState,
456 F: ExclusiveOption,
457{
458 fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
459 command.arg("-d");
460 force.inject_args(command);
461
462 if I::SET
463 && let Some(server_id) = &self.server_id
464 {
465 command.arg(format!("--serverid={server_id}"));
466 }
467
468 S::inject_shelved(command);
469 }
470}
471
472impl<T, W> ClientMode<Unselected, T, W> for StdoutMode
473where
474 T: ExclusiveOption,
475 W: ExclusiveOption,
476{
477 fn inject_mode_args(
478 &self,
479 command: &mut Command,
480 _: &Unselected,
481 template: &T,
482 workspace_type: &W,
483 ) {
484 command.arg("-o");
485 template.inject_args(command);
486 workspace_type.inject_args(command);
487 }
488}
489
490impl<O> ClientMode<Unselected, Unselected, Unselected> for StreamMode<O>
491where
492 O: ExclusiveOption,
493{
494 fn inject_mode_args(
495 &self,
496 command: &mut Command,
497 _: &Unselected,
498 _: &Unselected,
499 _: &Unselected,
500 ) {
501 command.arg("-S").arg(&self.stream);
502 self.output.inject_args(command);
503 }
504}
505
506impl<F> ClientMode<F, Unselected, Unselected> for SwitchViewMode<StreamSwitch>
507where
508 F: ExclusiveOption,
509{
510 fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
511 command.arg("-s");
512 force.inject_args(command);
513 self.view.inject_args(command);
514 }
515}
516
517impl<F> ClientMode<F, Unselected, Unselected> for SwitchViewMode<TemplateSwitch>
518where
519 F: ExclusiveOption,
520{
521 fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
522 command.arg("-s");
523 force.inject_args(command);
524 self.view.inject_args(command);
525 }
526}
527
528impl<F> ClientMode<F, Unselected, Unselected> for StdinMode
529where
530 F: ExclusiveOption,
531{
532 fn inject_mode_args(&self, command: &mut Command, force: &F, _: &Unselected, _: &Unselected) {
533 command.arg("-i");
534 force.inject_args(command);
535 }
536}
537
538#[cfg_attr(
543 feature = "lt2015_2",
544 doc = "```text",
545 doc = "p4 client [-f] [-t template] [clientname]",
546 doc = "p4 client -o [-t template] [clientname]",
547 doc = "p4 client -d [-f [-Fs]] clientname",
548 doc = "p4 client -s [-S stream | -t clientname] clientname",
549 doc = "p4 client -S stream [[-c change] -o] [clientname]",
550 doc = "p4 client -i [-f]",
551 doc = "```"
552)]
553#[cfg_attr(
554 all(feature = "lt2016_2", not(feature = "lt2015_2")),
555 doc = "```text",
556 doc = "p4 client [-f] [-t template] [clientname]",
557 doc = "p4 client -o [-t template] [clientname]",
558 doc = "p4 client -d [-f [-Fs]] clientname",
559 doc = "p4 client -s [-S stream | -t clientname] clientname",
560 doc = "p4 client -S stream [[-c change] -o] [clientname]",
561 doc = "p4 client -i [-f]",
562 doc = "p4 client -d -f --serverid=serverid [-Fs]",
563 doc = "```"
564)]
565#[cfg_attr(
566 all(feature = "lt2022_1", not(feature = "lt2016_2")),
567 doc = "```text",
568 doc = "p4 client [-f] [-t template] [-T type] [clientname]",
569 doc = "p4 client -o [-t template] [-T type] [clientname]",
570 doc = "p4 client -d [-f [-Fs]] clientname",
571 doc = "p4 client -s [-S stream | -t clientname] clientname",
572 doc = "p4 client -S stream [[-c change] -o] [clientname]",
573 doc = "p4 client -i [-f]",
574 doc = "p4 client -d -f --serverid=serverid [-Fs]",
575 doc = "```"
576)]
577#[cfg_attr(
578 all(feature = "lt2024_1", not(feature = "lt2022_1")),
579 doc = "```text",
580 doc = "p4 client [-f] [-t template] [-T type] [name]",
581 doc = "p4 client -d [-f [-Fs]] name",
582 doc = "p4 client -o [-t template] [-T type] [name]",
583 doc = "p4 client -S stream [[-c change] -o] [name]",
584 doc = "p4 client -s [-f] -S stream [name]",
585 doc = "p4 client -s [-f] -t template [name]",
586 doc = "p4 client -i [-f]",
587 doc = "p4 client -d -f --serverid=X [-Fs] name",
588 doc = "```"
589)]
590#[cfg_attr(
591 not(feature = "lt2024_1"),
592 doc = "```text",
593 doc = "p4 client [-f] [-t template] [-T type] [name]",
594 doc = "p4 client -d [-f [-Fs|-Fd]] name",
595 doc = "p4 client -o [-t template] [-T type] [name]",
596 doc = "p4 client -S stream [[-c change] -o] [name]",
597 doc = "p4 client -s [-f] -S stream [name]",
598 doc = "p4 client -s [-f] -t template [name]",
599 doc = "p4 client -i [-f]",
600 doc = "p4 client -d -f --serverid=X [-Fs] name",
601 doc = "```"
602)]
603#[cfg_attr(
607 feature = "lt2016_2",
608 doc = "The type parameters track the mode `M` and the orthogonal option dimensions `F` (`-f`) and `T` (`-t template`) at compile time, so a transition is only available when every option already set is valid in the target form:",
609 doc = "",
610 doc = "- [`Self::delete`] requires `-t` to be unset and ends in the terminal [`DeleteMode`] state, from which no further transitions are possible",
611 doc = "- [`Self::stdout`] requires `-f` to be unset; `-t` is carried into the [`StdoutMode`] state",
612 doc = "- [`Self::stream`] requires `-f` / `-t` to be unset",
613 doc = "- [`Self::switch_view_stream`] / [`Self::switch_view_template`] / [`Self::switch_view`] switch the view: for the stream form `-t` must be unset, while the template form requires `-t` to be set",
614 doc = "- the `-s -S stream` form has a second entry path: select the stream with [`Self::stream`] and then [`Self::switch_view`] (only before `-o` is selected)",
615 doc = "- [`Self::stdin`] requires `-t` to be unset; `-f` is carried into the [`StdinMode`] state"
616)]
617#[cfg_attr(
618 all(feature = "lt2022_1", not(feature = "lt2016_2")),
619 doc = "The type parameters track the mode `M` and the orthogonal option dimensions `F` (`-f`), `T` (`-t template`), and `W` (`-T type`) at compile time, so a transition is only available when every option already set is valid in the target form:",
620 doc = "",
621 doc = "- [`Self::delete`] requires `-t` / `-T` to be unset and ends in the terminal [`DeleteMode`] state, from which no further transitions are possible",
622 doc = "- [`Self::stdout`] requires `-f` to be unset; `-t` / `-T` are carried into the [`StdoutMode`] state",
623 doc = "- [`Self::stream`] requires `-f` / `-t` / `-T` to be unset",
624 doc = "- [`Self::switch_view_stream`] / [`Self::switch_view_template`] / [`Self::switch_view`] require `-T` to be unset; for the stream form `-t` must also be unset, while the template form requires `-t` to be set",
625 doc = "- the `-s -S stream` form has a second entry path: select the stream with [`Self::stream`] and then [`Self::switch_view`] (only before `-o` is selected)",
626 doc = "- [`Self::stdin`] requires `-t` / `-T` to be unset; `-f` is carried into the [`StdinMode`] state"
627)]
628#[cfg_attr(
629 not(feature = "lt2022_1"),
630 doc = "The type parameters track the mode `M` and the orthogonal option dimensions `F` (`-f`), `T` (`-t template`), and `W` (`-T type`) at compile time, so a transition is only available when every option already set is valid in the target form:",
631 doc = "",
632 doc = "- [`Self::delete`] requires `-t` / `-T` to be unset and ends in the terminal [`DeleteMode`] state, from which no further transitions are possible",
633 doc = "- [`Self::stdout`] requires `-f` to be unset; `-t` / `-T` are carried into the [`StdoutMode`] state",
634 doc = "- [`Self::stream`] requires `-f` / `-t` / `-T` to be unset",
635 doc = "- [`Self::switch_view_stream`] / [`Self::switch_view_template`] / [`Self::switch_view`] require `-T` to be unset; for the stream form `-t` must also be unset, while the template form requires `-t` to be set. `-f` is carried into the [`SwitchViewMode`] state",
636 doc = "- the `-s -S stream` form has a second entry path: select the stream with [`Self::stream`] and then [`Self::switch_view`] (only before `-o` is selected); `-f` is added afterwards if needed",
637 doc = "- [`Self::stdin`] requires `-t` / `-T` to be unset; `-f` is carried into the [`StdinMode`] state"
638)]
639#[derive(Debug, Clone, Default)]
640pub struct Client<M = GeneralMode, F = Unselected, T = Unselected, W = Unselected> {
641 bin: PathBuf,
642
643 global_opts: GlobalOpts,
644
645 mode: M,
646
647 force: F,
648
649 template: T,
650
651 workspace_type: W,
652}
653
654impl<M, F, T, W> SubCommand for Client<M, F, T, W>
655where
656 M: ClientMode<F, T, W>,
657{
658 fn name(&self) -> &str {
659 "client"
660 }
661
662 fn inject_local_args(&self, command: &mut Command) {
663 self.mode
664 .inject_mode_args(command, &self.force, &self.template, &self.workspace_type);
665 }
666
667 fn global_opts(&self) -> Option<&GlobalOpts> {
668 Some(&self.global_opts)
669 }
670}
671
672impl<F, T, W> ParameterizedSpawn<()> for Client<GeneralMode, F, T, W>
677where
678 F: ExclusiveOption,
679 T: ExclusiveOption,
680 W: ExclusiveOption,
681{
682 type Output = Child;
683 type Error = std::io::Error;
684
685 fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
692 self.setup_command(&self.bin)
693 .stdout(Stdio::piped())
694 .stderr(Stdio::piped())
695 .spawn()
696 }
697}
698
699impl<F, T, W, I> ParameterizedSpawn<(I,)> for Client<GeneralMode, F, T, W>
700where
701 F: ExclusiveOption,
702 T: ExclusiveOption,
703 W: ExclusiveOption,
704 I: AsRef<OsStr>,
705{
706 type Output = Child;
707 type Error = std::io::Error;
708
709 fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
716 self.setup_command(&self.bin)
717 .arg(name)
718 .stdout(Stdio::piped())
719 .stderr(Stdio::piped())
720 .spawn()
721 }
722}
723
724impl<T, W> ParameterizedSpawn<()> for Client<StdoutMode, Unselected, T, W>
729where
730 T: ExclusiveOption,
731 W: ExclusiveOption,
732{
733 type Output = Child;
734 type Error = std::io::Error;
735
736 fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
743 self.setup_command(&self.bin)
744 .stdout(Stdio::piped())
745 .stderr(Stdio::piped())
746 .spawn()
747 }
748}
749
750impl<T, W, I> ParameterizedSpawn<(I,)> for Client<StdoutMode, Unselected, T, W>
751where
752 T: ExclusiveOption,
753 W: ExclusiveOption,
754 I: AsRef<OsStr>,
755{
756 type Output = Child;
757 type Error = std::io::Error;
758
759 fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
765 self.setup_command(&self.bin)
766 .arg(name)
767 .stdout(Stdio::piped())
768 .stderr(Stdio::piped())
769 .spawn()
770 }
771}
772
773impl<I, S, F, A> ParameterizedSpawn<(A,)> for Client<DeleteMode<I, S>, F, Unselected, Unselected>
778where
779 I: ServerIdState,
780 S: ShelvedState,
781 F: ExclusiveOption,
782 (I, S, F): ValidDeleteCombination,
783 A: AsRef<OsStr>,
784{
785 type Output = Child;
786 type Error = std::io::Error;
787
788 fn spawn_with(&mut self, (name,): (A,)) -> Result<Self::Output, Self::Error> {
793 self.setup_command(&self.bin)
794 .arg(name)
795 .stdout(Stdio::piped())
796 .stderr(Stdio::piped())
797 .spawn()
798 }
799}
800
801impl<O> ParameterizedSpawn<()> for Client<StreamMode<O>, Unselected, Unselected, Unselected>
806where
807 O: ExclusiveOption,
808{
809 type Output = Child;
810 type Error = std::io::Error;
811
812 fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
818 self.setup_command(&self.bin)
819 .stdout(Stdio::piped())
820 .stderr(Stdio::piped())
821 .spawn()
822 }
823}
824
825impl<O, I> ParameterizedSpawn<(I,)> for Client<StreamMode<O>, Unselected, Unselected, Unselected>
826where
827 O: ExclusiveOption,
828 I: AsRef<OsStr>,
829{
830 type Output = Child;
831 type Error = std::io::Error;
832
833 fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
839 self.setup_command(&self.bin)
840 .arg(name)
841 .stdout(Stdio::piped())
842 .stderr(Stdio::piped())
843 .spawn()
844 }
845}
846
847impl<V, F> ParameterizedSpawn<()> for Client<SwitchViewMode<V>, F, Unselected, Unselected>
853where
854 SwitchViewMode<V>: ClientMode<F, Unselected, Unselected>,
855 F: ExclusiveOption,
856{
857 type Output = Child;
858 type Error = std::io::Error;
859
860 fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
866 self.setup_command(&self.bin)
867 .stdout(Stdio::piped())
868 .stderr(Stdio::piped())
869 .spawn()
870 }
871}
872
873impl<V, F, I> ParameterizedSpawn<(I,)> for Client<SwitchViewMode<V>, F, Unselected, Unselected>
874where
875 SwitchViewMode<V>: ClientMode<F, Unselected, Unselected>,
876 F: ExclusiveOption,
877 I: AsRef<OsStr>,
878{
879 type Output = Child;
880 type Error = std::io::Error;
881
882 fn spawn_with(&mut self, (name,): (I,)) -> Result<Self::Output, Self::Error> {
888 self.setup_command(&self.bin)
889 .arg(name)
890 .stdout(Stdio::piped())
891 .stderr(Stdio::piped())
892 .spawn()
893 }
894}
895
896impl<F> ParameterizedSpawn<()> for Client<StdinMode, F, Unselected, Unselected>
901where
902 F: ExclusiveOption,
903{
904 type Output = Child;
905 type Error = std::io::Error;
906
907 fn spawn_with(&mut self, (): ()) -> Result<Self::Output, Self::Error> {
911 self.setup_command(&self.bin)
912 .stdin(Stdio::piped())
913 .stdout(Stdio::piped())
914 .stderr(Stdio::piped())
915 .spawn()
916 }
917}
918
919impl<F> ParameterizedSpawn<(Stdio,)> for Client<StdinMode, F, Unselected, Unselected>
920where
921 F: ExclusiveOption,
922{
923 type Output = Child;
924 type Error = std::io::Error;
925
926 fn spawn_with(&mut self, (stdio,): (Stdio,)) -> Result<Self::Output, Self::Error> {
935 self.setup_command(&self.bin)
936 .stdin(stdio)
937 .stdout(Stdio::piped())
938 .stderr(Stdio::piped())
939 .spawn()
940 }
941}
942
943impl Client<GeneralMode, Unselected, Unselected, Unselected> {
948 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
952 Self {
953 bin: bin.into(),
954 global_opts,
955 mode: GeneralMode,
956 force: Unselected,
957 template: Unselected,
958 workspace_type: Unselected,
959 }
960 }
961}
962
963impl<M, F, T, W> Client<M, F, T, W> {
964 pub fn get_bin(&self) -> &Path {
966 &self.bin
967 }
968
969 pub fn set_bin(&mut self, bin: impl Into<PathBuf>) -> &mut Self {
971 self.bin = bin.into();
972 self
973 }
974
975 pub fn bin(mut self, bin: impl Into<PathBuf>) -> Self {
977 self.set_bin(bin);
978 self
979 }
980
981 pub fn get_global_opts(&self) -> &GlobalOpts {
983 &self.global_opts
984 }
985
986 pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
988 self.global_opts = v;
989 self
990 }
991
992 pub fn global_opts(mut self, v: GlobalOpts) -> Self {
994 self.set_global_opts(v);
995 self
996 }
997}
998
999impl<T, W> Client<GeneralMode, Unselected, T, W> {
1003 pub fn force(self) -> Client<GeneralMode, ForceSet, T, W> {
1012 Client {
1013 bin: self.bin,
1014 global_opts: self.global_opts,
1015 mode: self.mode,
1016 force: ForceSet,
1017 template: self.template,
1018 workspace_type: self.workspace_type,
1019 }
1020 }
1021}
1022
1023impl<I, S> Client<DeleteMode<I, S>, Unselected, Unselected, Unselected>
1024where
1025 I: ServerIdState,
1026 S: ShelvedState,
1027{
1028 #[cfg_attr(
1037 feature = "lt2015_2",
1038 doc = "`-f` is required by the `-Fs` option, but it may be selected before or after it; calling `force` on a state carrying it completes the documented form and makes the command spawnable."
1039 )]
1040 #[cfg_attr(
1041 all(feature = "lt2024_1", not(feature = "lt2015_2")),
1042 doc = "`-f` is required by the `-Fs` and `--serverid=X` options, but it may be selected before or after them; calling `force` on a state carrying those options completes the documented form and makes the command spawnable."
1043 )]
1044 #[cfg_attr(
1045 not(feature = "lt2024_1"),
1046 doc = "`-f` is required by the `-Fs`, `-Fd`, and `--serverid=X` options, but it may be selected before or after them; calling `force` on a state carrying those options completes the documented form and makes the command spawnable."
1047 )]
1048 pub fn force(self) -> Client<DeleteMode<I, S>, ForceSet, Unselected, Unselected> {
1049 Client {
1050 bin: self.bin,
1051 global_opts: self.global_opts,
1052 mode: self.mode,
1053 force: ForceSet,
1054 template: self.template,
1055 workspace_type: self.workspace_type,
1056 }
1057 }
1058}
1059
1060#[cfg(not(feature = "lt2022_1"))]
1061impl<V> Client<SwitchViewMode<V>, Unselected, Unselected, Unselected> {
1062 pub fn force(self) -> Client<SwitchViewMode<V>, ForceSet, Unselected, Unselected> {
1069 Client {
1070 bin: self.bin,
1071 global_opts: self.global_opts,
1072 mode: self.mode,
1073 force: ForceSet,
1074 template: self.template,
1075 workspace_type: self.workspace_type,
1076 }
1077 }
1078}
1079
1080impl Client<StdinMode, Unselected, Unselected, Unselected> {
1081 pub fn force(self) -> Client<StdinMode, ForceSet, Unselected, Unselected> {
1089 Client {
1090 bin: self.bin,
1091 global_opts: self.global_opts,
1092 mode: self.mode,
1093 force: ForceSet,
1094 template: self.template,
1095 workspace_type: self.workspace_type,
1096 }
1097 }
1098}
1099
1100impl<F, W> Client<GeneralMode, F, Unselected, W> {
1103 pub fn template(self, template: impl Into<String>) -> Client<GeneralMode, F, TemplateSet, W> {
1112 Client {
1113 bin: self.bin,
1114 global_opts: self.global_opts,
1115 mode: self.mode,
1116 force: self.force,
1117 template: TemplateSet {
1118 template: template.into(),
1119 },
1120 workspace_type: self.workspace_type,
1121 }
1122 }
1123}
1124
1125impl<W> Client<StdoutMode, Unselected, Unselected, W> {
1126 pub fn template(
1133 self,
1134 template: impl Into<String>,
1135 ) -> Client<StdoutMode, Unselected, TemplateSet, W> {
1136 Client {
1137 bin: self.bin,
1138 global_opts: self.global_opts,
1139 mode: self.mode,
1140 force: self.force,
1141 template: TemplateSet {
1142 template: template.into(),
1143 },
1144 workspace_type: self.workspace_type,
1145 }
1146 }
1147}
1148
1149#[cfg(not(feature = "lt2016_2"))]
1152impl<F, T> Client<GeneralMode, F, T, Unselected> {
1153 pub fn workspace_type(
1160 self,
1161 workspace_type: WorkSpaceType,
1162 ) -> Client<GeneralMode, F, T, TypeSet> {
1163 Client {
1164 bin: self.bin,
1165 global_opts: self.global_opts,
1166 mode: self.mode,
1167 force: self.force,
1168 template: self.template,
1169 workspace_type: TypeSet { workspace_type },
1170 }
1171 }
1172}
1173
1174#[cfg(not(feature = "lt2016_2"))]
1175impl<T> Client<StdoutMode, Unselected, T, Unselected> {
1176 pub fn workspace_type(
1183 self,
1184 workspace_type: WorkSpaceType,
1185 ) -> Client<StdoutMode, Unselected, T, TypeSet> {
1186 Client {
1187 bin: self.bin,
1188 global_opts: self.global_opts,
1189 mode: self.mode,
1190 force: self.force,
1191 template: self.template,
1192 workspace_type: TypeSet { workspace_type },
1193 }
1194 }
1195}
1196
1197impl<M, F, W> Client<M, F, TemplateSet, W> {
1200 pub fn get_template(&self) -> &str {
1202 &self.template.template
1203 }
1204
1205 pub fn set_template(&mut self, template: impl Into<String>) -> &mut Self {
1212 self.template.template = template.into();
1213 self
1214 }
1215}
1216
1217#[cfg(not(feature = "lt2016_2"))]
1218impl<M, F, T> Client<M, F, T, TypeSet> {
1219 pub fn get_workspace_type(&self) -> WorkSpaceType {
1221 self.workspace_type.workspace_type
1222 }
1223
1224 pub fn set_workspace_type(&mut self, workspace_type: WorkSpaceType) -> &mut Self {
1231 self.workspace_type.workspace_type = workspace_type;
1232 self
1233 }
1234}
1235
1236impl<F> Client<GeneralMode, F, Unselected, Unselected> {
1241 pub fn delete(self) -> Client<DeleteMode<Unselected, Unselected>, F, Unselected, Unselected> {
1252 Client {
1253 bin: self.bin,
1254 global_opts: self.global_opts,
1255 mode: DeleteMode {
1256 server_id: None,
1257 serverid_state: PhantomData,
1258 shelved: Unselected,
1259 },
1260 force: self.force,
1261 template: self.template,
1262 workspace_type: self.workspace_type,
1263 }
1264 }
1265
1266 pub fn stdin(self) -> Client<StdinMode, F, Unselected, Unselected> {
1275 Client {
1276 bin: self.bin,
1277 global_opts: self.global_opts,
1278 mode: StdinMode,
1279 force: self.force,
1280 template: self.template,
1281 workspace_type: self.workspace_type,
1282 }
1283 }
1284}
1285
1286impl<F> Client<GeneralMode, F, Unselected, Unselected>
1290where
1291 F: SwitchViewForceCompatible,
1292{
1293 #[cfg_attr(
1301 not(feature = "lt2022_1"),
1302 doc = "",
1303 doc = "The `-f` option may be selected before this transition, or added afterwards with [`Client::force`]."
1304 )]
1305 pub fn switch_view_stream(
1306 self,
1307 stream: impl Into<String>,
1308 ) -> Client<SwitchViewMode<StreamSwitch>, F, Unselected, Unselected> {
1309 Client {
1310 bin: self.bin,
1311 global_opts: self.global_opts,
1312 mode: SwitchViewMode {
1313 view: StreamSwitch {
1314 stream: stream.into(),
1315 },
1316 },
1317 force: self.force,
1318 template: self.template,
1319 workspace_type: self.workspace_type,
1320 }
1321 }
1322
1323 #[cfg_attr(
1332 not(feature = "lt2022_1"),
1333 doc = "",
1334 doc = "The `-f` option may be selected before this transition, or added afterwards with [`Client::force`]."
1335 )]
1336 pub fn switch_view_template(
1337 self,
1338 template: impl Into<String>,
1339 ) -> Client<SwitchViewMode<TemplateSwitch>, F, Unselected, Unselected> {
1340 Client {
1341 bin: self.bin,
1342 global_opts: self.global_opts,
1343 mode: SwitchViewMode {
1344 view: TemplateSwitch {
1345 template: template.into(),
1346 },
1347 },
1348 force: self.force,
1349 template: self.template,
1350 workspace_type: self.workspace_type,
1351 }
1352 }
1353}
1354
1355impl<F> Client<GeneralMode, F, TemplateSet, Unselected>
1356where
1357 F: SwitchViewForceCompatible,
1358{
1359 #[cfg_attr(
1368 not(feature = "lt2022_1"),
1369 doc = "",
1370 doc = "The `-f` option may be selected before this transition, or added afterwards with [`Client::force`]."
1371 )]
1372 pub fn switch_view(self) -> Client<SwitchViewMode<TemplateSwitch>, F, Unselected, Unselected> {
1373 Client {
1374 bin: self.bin,
1375 global_opts: self.global_opts,
1376 mode: SwitchViewMode {
1377 view: TemplateSwitch {
1378 template: self.template.template,
1379 },
1380 },
1381 force: self.force,
1382 template: Unselected,
1383 workspace_type: self.workspace_type,
1384 }
1385 }
1386}
1387
1388impl<T, W> Client<GeneralMode, Unselected, T, W> {
1389 pub fn stdout(self) -> Client<StdoutMode, Unselected, T, W> {
1398 Client {
1399 bin: self.bin,
1400 global_opts: self.global_opts,
1401 mode: StdoutMode,
1402 force: self.force,
1403 template: self.template,
1404 workspace_type: self.workspace_type,
1405 }
1406 }
1407}
1408
1409impl Client<GeneralMode, Unselected, Unselected, Unselected> {
1410 pub fn stream(
1419 self,
1420 stream: impl Into<String>,
1421 ) -> Client<StreamMode<Unselected>, Unselected, Unselected, Unselected> {
1422 Client {
1423 bin: self.bin,
1424 global_opts: self.global_opts,
1425 mode: StreamMode {
1426 stream: stream.into(),
1427 output: Unselected,
1428 },
1429 force: self.force,
1430 template: self.template,
1431 workspace_type: self.workspace_type,
1432 }
1433 }
1434}
1435
1436impl<F> Client<GeneralMode, F, Unselected, Unselected>
1444where
1445 F: ExclusiveOption,
1446{
1447 #[cfg(not(feature = "lt2015_2"))]
1461 pub fn server_id(
1462 self,
1463 server_id: impl Into<String>,
1464 ) -> Client<DeleteMode<ServerIdSet, Unselected>, F, Unselected, Unselected> {
1465 Client {
1466 bin: self.bin,
1467 global_opts: self.global_opts,
1468 mode: DeleteMode {
1469 server_id: Some(server_id.into()),
1470 serverid_state: PhantomData,
1471 shelved: Unselected,
1472 },
1473 force: self.force,
1474 template: self.template,
1475 workspace_type: self.workspace_type,
1476 }
1477 }
1478
1479 pub fn keep_shelved(
1491 self,
1492 ) -> Client<DeleteMode<Unselected, ShelvedKept>, F, Unselected, Unselected> {
1493 Client {
1494 bin: self.bin,
1495 global_opts: self.global_opts,
1496 mode: DeleteMode {
1497 server_id: None,
1498 serverid_state: PhantomData,
1499 shelved: ShelvedKept,
1500 },
1501 force: self.force,
1502 template: self.template,
1503 workspace_type: self.workspace_type,
1504 }
1505 }
1506
1507 #[cfg(not(feature = "lt2024_1"))]
1519 pub fn delete_shelved(
1520 self,
1521 ) -> Client<DeleteMode<Unselected, ShelvedDeleted>, F, Unselected, Unselected> {
1522 Client {
1523 bin: self.bin,
1524 global_opts: self.global_opts,
1525 mode: DeleteMode {
1526 server_id: None,
1527 serverid_state: PhantomData,
1528 shelved: ShelvedDeleted,
1529 },
1530 force: self.force,
1531 template: self.template,
1532 workspace_type: self.workspace_type,
1533 }
1534 }
1535}
1536
1537#[cfg(not(feature = "lt2015_2"))]
1546impl<S, F> Client<DeleteMode<Unselected, S>, F, Unselected, Unselected>
1547where
1548 S: ServerIdCompatible,
1549 F: ExclusiveOption,
1550{
1551 pub fn server_id(
1566 self,
1567 server_id: impl Into<String>,
1568 ) -> Client<DeleteMode<ServerIdSet, S>, F, Unselected, Unselected> {
1569 Client {
1570 bin: self.bin,
1571 global_opts: self.global_opts,
1572 mode: DeleteMode {
1573 server_id: Some(server_id.into()),
1574 serverid_state: PhantomData,
1575 shelved: self.mode.shelved,
1576 },
1577 force: self.force,
1578 template: self.template,
1579 workspace_type: self.workspace_type,
1580 }
1581 }
1582}
1583
1584#[cfg(not(feature = "lt2015_2"))]
1587impl<S, F> Client<DeleteMode<ServerIdSet, S>, F, Unselected, Unselected>
1588where
1589 S: ServerIdCompatible,
1590 F: ExclusiveOption,
1591{
1592 pub fn get_server_id(&self) -> &str {
1594 self.mode.server_id.as_deref().expect("server ID is set")
1595 }
1596
1597 pub fn set_server_id(&mut self, server_id: impl Into<String>) -> &mut Self {
1604 self.mode.server_id = Some(server_id.into());
1605 self
1606 }
1607}
1608
1609impl<I, S, F> Client<DeleteMode<I, S>, F, Unselected, Unselected>
1612where
1613 I: ServerIdState,
1614 S: ShelvedState,
1615 F: ExclusiveOption,
1616{
1617 pub fn get_shelved(&self) -> Option<ShelvedHandling> {
1620 S::handling()
1621 }
1622}
1623
1624impl<I, F> Client<DeleteMode<I, Unselected>, F, Unselected, Unselected>
1629where
1630 I: ServerIdState,
1631 F: ExclusiveOption,
1632{
1633 pub fn keep_shelved(self) -> Client<DeleteMode<I, ShelvedKept>, F, Unselected, Unselected> {
1645 Client {
1646 bin: self.bin,
1647 global_opts: self.global_opts,
1648 mode: DeleteMode {
1649 server_id: self.mode.server_id,
1650 serverid_state: PhantomData,
1651 shelved: ShelvedKept,
1652 },
1653 force: self.force,
1654 template: self.template,
1655 workspace_type: self.workspace_type,
1656 }
1657 }
1658}
1659
1660#[cfg(not(feature = "lt2024_1"))]
1664impl<F> Client<DeleteMode<Unselected, Unselected>, F, Unselected, Unselected>
1665where
1666 F: ExclusiveOption,
1667{
1668 pub fn delete_shelved(
1681 self,
1682 ) -> Client<DeleteMode<Unselected, ShelvedDeleted>, F, Unselected, Unselected> {
1683 Client {
1684 bin: self.bin,
1685 global_opts: self.global_opts,
1686 mode: DeleteMode {
1687 server_id: None,
1688 serverid_state: PhantomData,
1689 shelved: ShelvedDeleted,
1690 },
1691 force: self.force,
1692 template: self.template,
1693 workspace_type: self.workspace_type,
1694 }
1695 }
1696}
1697
1698impl<O> Client<StreamMode<O>, Unselected, Unselected, Unselected> {
1703 pub fn get_stream(&self) -> &str {
1705 &self.mode.stream
1706 }
1707
1708 pub fn set_stream(&mut self, stream: impl Into<String>) -> &mut Self {
1715 self.mode.stream = stream.into();
1716 self
1717 }
1718
1719 pub fn stream(mut self, stream: impl Into<String>) -> Self {
1726 self.set_stream(stream);
1727 self
1728 }
1729}
1730
1731impl Client<StreamMode<Unselected>, Unselected, Unselected, Unselected> {
1736 #[cfg_attr(
1747 not(feature = "lt2022_1"),
1748 doc = "The optional `-f` can be added after this transition with [`Client::force`]."
1749 )]
1750 pub fn switch_view(
1751 self,
1752 ) -> Client<SwitchViewMode<StreamSwitch>, Unselected, Unselected, Unselected> {
1753 Client {
1754 bin: self.bin,
1755 global_opts: self.global_opts,
1756 mode: SwitchViewMode {
1757 view: StreamSwitch {
1758 stream: self.mode.stream,
1759 },
1760 },
1761 force: self.force,
1762 template: self.template,
1763 workspace_type: self.workspace_type,
1764 }
1765 }
1766
1767 pub fn stdout(self) -> Client<StreamMode<StdoutOutput>, Unselected, Unselected, Unselected> {
1776 Client {
1777 bin: self.bin,
1778 global_opts: self.global_opts,
1779 mode: StreamMode {
1780 stream: self.mode.stream,
1781 output: StdoutOutput { change: None },
1782 },
1783 force: self.force,
1784 template: self.template,
1785 workspace_type: self.workspace_type,
1786 }
1787 }
1788}
1789
1790impl Client<StreamMode<StdoutOutput>, Unselected, Unselected, Unselected> {
1791 pub fn get_change(&self) -> Option<&str> {
1793 self.mode.output.change.as_deref()
1794 }
1795
1796 pub fn set_change(&mut self, change: impl Into<String>) -> &mut Self {
1804 self.mode.output.change = Some(change.into());
1805 self
1806 }
1807
1808 pub fn change(mut self, change: impl Into<String>) -> Self {
1816 self.set_change(change);
1817 self
1818 }
1819}
1820
1821impl<F> Client<SwitchViewMode<StreamSwitch>, F, Unselected, Unselected> {
1826 pub fn get_stream(&self) -> &str {
1828 &self.mode.view.stream
1829 }
1830
1831 pub fn set_stream(&mut self, stream: impl Into<String>) -> &mut Self {
1837 self.mode.view.stream = stream.into();
1838 self
1839 }
1840
1841 pub fn stream(mut self, stream: impl Into<String>) -> Self {
1847 self.set_stream(stream);
1848 self
1849 }
1850}
1851
1852impl<F> Client<SwitchViewMode<TemplateSwitch>, F, Unselected, Unselected> {
1853 pub fn get_template(&self) -> &str {
1855 &self.mode.view.template
1856 }
1857
1858 pub fn set_template(&mut self, template: impl Into<String>) -> &mut Self {
1865 self.mode.view.template = template.into();
1866 self
1867 }
1868
1869 pub fn template(mut self, template: impl Into<String>) -> Self {
1876 self.set_template(template);
1877 self
1878 }
1879}
1880
1881#[cfg(test)]
1882mod tests {
1883 use super::*;
1884 use crate::cmd::args_of;
1885
1886 #[test]
1889 fn general_mode_without_options() {
1890 let client = Client::new("p4", GlobalOpts::new());
1891
1892 assert_eq!(args_of(&client.setup_command("p4")), ["client"]);
1893 }
1894
1895 #[test]
1896 fn general_mode_with_force_and_template() {
1897 let client = Client::new("p4", GlobalOpts::new()).force().template("tpl");
1898
1899 assert_eq!(
1900 args_of(&client.setup_command("p4")),
1901 ["client", "-f", "-t", "tpl"]
1902 );
1903 assert_eq!(client.get_template(), "tpl");
1904
1905 let mut client = Client::new("p4", GlobalOpts::new()).template("tmp");
1907 client.set_template("tpl");
1908 assert_eq!(client.get_template(), "tpl");
1909 assert_eq!(
1910 args_of(&client.setup_command("p4")),
1911 ["client", "-t", "tpl"]
1912 );
1913 }
1914
1915 #[cfg(not(feature = "lt2016_2"))]
1916 #[test]
1917 fn general_mode_with_all_options() {
1918 let client = Client::new("p4", GlobalOpts::new())
1919 .force()
1920 .template("tpl")
1921 .workspace_type(WorkSpaceType::Readonly);
1922
1923 assert_eq!(
1924 args_of(&client.setup_command("p4")),
1925 ["client", "-f", "-t", "tpl", "-T", "readonly"]
1926 );
1927 assert_eq!(client.get_template(), "tpl");
1928 assert_eq!(client.get_workspace_type(), WorkSpaceType::Readonly);
1929 }
1930
1931 #[cfg(not(feature = "lt2016_2"))]
1932 #[test]
1933 fn workspace_type_values() {
1934 for (ty, s) in [
1935 (WorkSpaceType::Writable, "writeable"),
1936 (WorkSpaceType::Readonly, "readonly"),
1937 (WorkSpaceType::Partitioned, "partitioned"),
1938 #[cfg(not(feature = "lt2024_1"))]
1939 (WorkSpaceType::PartitionedJnl, "partitioned-jnl"),
1940 #[cfg(not(feature = "lt2024_1"))]
1941 (WorkSpaceType::Graph, "graph"),
1942 ] {
1943 let client = Client::new("p4", GlobalOpts::new()).workspace_type(ty);
1944 assert_eq!(args_of(&client.setup_command("p4")), ["client", "-T", s]);
1945 }
1946 }
1947
1948 #[test]
1949 fn stdout_mode_carries_template() {
1950 let client = Client::new("p4", GlobalOpts::new())
1951 .template("tpl")
1952 .stdout();
1953
1954 assert_eq!(
1955 args_of(&client.setup_command("p4")),
1956 ["client", "-o", "-t", "tpl"]
1957 );
1958 }
1959
1960 #[cfg(not(feature = "lt2016_2"))]
1961 #[test]
1962 fn stdout_mode_carries_template_and_type() {
1963 let client = Client::new("p4", GlobalOpts::new())
1964 .template("tpl")
1965 .workspace_type(WorkSpaceType::Partitioned)
1966 .stdout();
1967
1968 assert_eq!(
1969 args_of(&client.setup_command("p4")),
1970 ["client", "-o", "-t", "tpl", "-T", "partitioned"]
1971 );
1972 }
1973
1974 #[test]
1975 fn delete_mode_is_terminal() {
1976 let client = Client::new("p4", GlobalOpts::new()).delete();
1977
1978 assert_eq!(args_of(&client.setup_command("p4")), ["client", "-d"]);
1979 }
1980
1981 #[test]
1982 fn delete_mode_with_force_and_keep_shelved() {
1983 let client = Client::new("p4", GlobalOpts::new())
1984 .delete()
1985 .force()
1986 .keep_shelved();
1987
1988 assert_eq!(client.get_shelved(), Some(ShelvedHandling::Keep));
1989 assert_eq!(
1990 args_of(&client.setup_command("p4")),
1991 ["client", "-d", "-f", "-Fs"]
1992 );
1993 }
1994
1995 #[cfg(not(feature = "lt2015_2"))]
1996 #[test]
1997 fn delete_mode_with_force_and_server_id() {
1998 let mut client = Client::new("p4", GlobalOpts::new())
2001 .delete()
2002 .force()
2003 .server_id("edge.1667");
2004
2005 assert_eq!(client.get_server_id(), "edge.1667");
2006 assert_eq!(client.get_shelved(), None);
2007 client.set_server_id("edge.42");
2008
2009 let client = client.keep_shelved();
2010
2011 assert_eq!(client.get_server_id(), "edge.42");
2012 assert_eq!(client.get_shelved(), Some(ShelvedHandling::Keep));
2013 assert_eq!(
2014 args_of(&client.setup_command("p4")),
2015 ["client", "-d", "-f", "--serverid=edge.42", "-Fs"]
2016 );
2017
2018 let client = Client::new("p4", GlobalOpts::new())
2021 .delete()
2022 .force()
2023 .keep_shelved()
2024 .server_id("edge.1667");
2025
2026 assert_eq!(client.get_server_id(), "edge.1667");
2027 assert_eq!(
2028 args_of(&client.setup_command("p4")),
2029 ["client", "-d", "-f", "--serverid=edge.1667", "-Fs"]
2030 );
2031 }
2032
2033 #[cfg(not(feature = "lt2024_1"))]
2034 #[test]
2035 fn delete_mode_with_force_and_delete_shelved() {
2036 let client = Client::new("p4", GlobalOpts::new())
2038 .delete()
2039 .force()
2040 .delete_shelved();
2041
2042 assert_eq!(client.get_shelved(), Some(ShelvedHandling::Delete));
2043 assert_eq!(
2044 args_of(&client.setup_command("p4")),
2045 ["client", "-d", "-f", "-Fd"]
2046 );
2047 }
2048
2049 #[test]
2050 fn force_set_in_general_mode_can_delete() {
2051 let client = Client::new("p4", GlobalOpts::new()).force().delete();
2052
2053 assert_eq!(args_of(&client.setup_command("p4")), ["client", "-d", "-f"]);
2054 }
2055
2056 #[test]
2057 fn force_unlocks_keep_shelved_one_shot() {
2058 let client = Client::new("p4", GlobalOpts::new()).force().keep_shelved();
2059
2060 assert_eq!(client.get_shelved(), Some(ShelvedHandling::Keep));
2061 assert_eq!(
2062 args_of(&client.setup_command("p4")),
2063 ["client", "-d", "-f", "-Fs"]
2064 );
2065 }
2066
2067 #[cfg(not(feature = "lt2015_2"))]
2068 #[test]
2069 fn force_unlocks_server_id_one_shot() {
2070 let client = Client::new("p4", GlobalOpts::new())
2071 .force()
2072 .server_id("edge.1667")
2073 .keep_shelved();
2074
2075 assert_eq!(client.get_server_id(), "edge.1667");
2076 assert_eq!(client.get_shelved(), Some(ShelvedHandling::Keep));
2077 assert_eq!(
2078 args_of(&client.setup_command("p4")),
2079 ["client", "-d", "-f", "--serverid=edge.1667", "-Fs"]
2080 );
2081 }
2082
2083 #[cfg(not(feature = "lt2024_1"))]
2084 #[test]
2085 fn force_unlocks_delete_shelved_one_shot() {
2086 let client = Client::new("p4", GlobalOpts::new())
2087 .force()
2088 .delete_shelved();
2089
2090 assert_eq!(client.get_shelved(), Some(ShelvedHandling::Delete));
2091 assert_eq!(
2092 args_of(&client.setup_command("p4")),
2093 ["client", "-d", "-f", "-Fd"]
2094 );
2095 }
2096
2097 #[test]
2098 fn stream_mode() {
2099 let client = Client::new("p4", GlobalOpts::new()).stream("//stream/main");
2100
2101 assert_eq!(
2102 args_of(&client.setup_command("p4")),
2103 ["client", "-S", "//stream/main"]
2104 );
2105 assert_eq!(client.get_stream(), "//stream/main");
2106
2107 let client = client.stdout();
2108
2109 assert_eq!(
2110 args_of(&client.setup_command("p4")),
2111 ["client", "-S", "//stream/main", "-o"]
2112 );
2113
2114 let client = client.change("4321");
2115
2116 assert_eq!(client.get_change(), Some("4321"));
2117 assert_eq!(
2118 args_of(&client.setup_command("p4")),
2119 ["client", "-S", "//stream/main", "-c", "4321", "-o"]
2120 );
2121 }
2122
2123 #[cfg(not(feature = "lt2022_1"))]
2124 #[test]
2125 fn switch_view_stream_carries_force() {
2126 let client = Client::new("p4", GlobalOpts::new())
2127 .force()
2128 .switch_view_stream("//stream/dev");
2129
2130 assert_eq!(
2131 args_of(&client.setup_command("p4")),
2132 ["client", "-s", "-f", "-S", "//stream/dev"]
2133 );
2134 assert_eq!(client.get_stream(), "//stream/dev");
2135 }
2136
2137 #[test]
2138 fn switch_view_reuses_stream() {
2139 let client = Client::new("p4", GlobalOpts::new())
2142 .stream("//stream/dev")
2143 .switch_view();
2144
2145 assert_eq!(
2146 args_of(&client.setup_command("p4")),
2147 ["client", "-s", "-S", "//stream/dev"]
2148 );
2149 assert_eq!(client.get_stream(), "//stream/dev");
2150
2151 let client = Client::new("p4", GlobalOpts::new())
2153 .stream("old")
2154 .stream("//stream/dev")
2155 .switch_view();
2156
2157 assert_eq!(
2158 args_of(&client.setup_command("p4")),
2159 ["client", "-s", "-S", "//stream/dev"]
2160 );
2161 }
2162
2163 #[cfg(not(feature = "lt2022_1"))]
2164 #[test]
2165 fn switch_view_reuses_stream_then_force() {
2166 let client = Client::new("p4", GlobalOpts::new())
2169 .stream("//stream/dev")
2170 .switch_view()
2171 .force();
2172
2173 assert_eq!(
2174 args_of(&client.setup_command("p4")),
2175 ["client", "-s", "-f", "-S", "//stream/dev"]
2176 );
2177 }
2178
2179 #[test]
2180 fn switch_view_template() {
2181 let client = Client::new("p4", GlobalOpts::new()).switch_view_template("tpl");
2182
2183 assert_eq!(
2184 args_of(&client.setup_command("p4")),
2185 ["client", "-s", "-t", "tpl"]
2186 );
2187 assert_eq!(client.get_template(), "tpl");
2188 }
2189
2190 #[test]
2191 fn switch_view_reuses_template() {
2192 let client = Client::new("p4", GlobalOpts::new())
2193 .template("tpl")
2194 .switch_view();
2195
2196 assert_eq!(
2197 args_of(&client.setup_command("p4")),
2198 ["client", "-s", "-t", "tpl"]
2199 );
2200 assert_eq!(client.get_template(), "tpl");
2201 }
2202
2203 #[test]
2204 fn stdin_mode() {
2205 let client = Client::new("p4", GlobalOpts::new()).stdin();
2206
2207 assert_eq!(args_of(&client.setup_command("p4")), ["client", "-i"]);
2208
2209 let client = Client::new("p4", GlobalOpts::new()).force().stdin();
2210
2211 assert_eq!(args_of(&client.setup_command("p4")), ["client", "-i", "-f"]);
2212 }
2213
2214 #[test]
2215 fn global_opts_are_injected_once() {
2216 let client = Client::new("p4", GlobalOpts::new().port("localhost:1666"));
2217
2218 assert_eq!(
2219 args_of(&client.setup_command("p4")),
2220 ["-p", "localhost:1666", "client"]
2221 );
2222 }
2223}