Skip to main content

perforce_cli/
cmd.rs

1pub mod add;
2pub mod admin;
3#[cfg(not(feature = "lt2016_1"))]
4pub mod aliases;
5pub mod annotate;
6pub mod archive;
7pub mod attribute;
8pub mod changes;
9pub mod describe;
10pub mod edit;
11pub mod filelog;
12pub mod print;
13pub mod sync;
14pub mod r#where;
15
16pub use add::Add;
17pub use admin::AdminEntry;
18#[cfg(not(feature = "lt2016_1"))]
19pub use aliases::Aliases;
20pub use annotate::Annotate;
21pub use archive::Archive;
22pub use attribute::Attribute;
23pub use changes::Changes;
24pub use describe::Describe;
25pub use edit::Edit;
26pub use filelog::FileLog;
27pub use print::Print;
28pub use sync::Sync;
29pub use r#where::Where;
30
31use std::{ffi::OsStr, process::Command};
32
33use crate::global::GlobalOpts;
34
35/// Long output mode for changelist descriptions shared by commands such as
36/// `p4 changes` and `p4 filelog`.
37///
38/// By default, only the first 30 (or 31) characters of the description are
39/// shown.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum LongOutput {
42    /// Show the full text of each changelist description (`-l`).
43    Default,
44    /// Show the full text truncated at 250 characters (`-L`).
45    Truncated,
46}
47
48impl LongOutput {
49    /// Returns the command-line flag for this long output mode.
50    pub fn as_str(&self) -> &'static str {
51        match self {
52            LongOutput::Default => "-l",
53            LongOutput::Truncated => "-L",
54        }
55    }
56}
57
58/// The default "no option selected" variant, shared by every
59/// [`ExclusiveOption`] group.
60///
61/// It is a zero-sized type whose [`ExclusiveOption::inject_args`] is a no-op,
62/// so any mutually exclusive option group can use it as its default type
63/// parameter instead of defining its own empty variant.
64#[derive(Debug, Clone, Copy, Default)]
65pub struct Unselected;
66
67impl ExclusiveOption for Unselected {
68    fn inject_args(&self, _: &mut Command) {}
69}
70
71/// Marker trait for a mutually exclusive option group.
72///
73/// Some Perforce commands accept a set of options where only one may be
74/// selected at a time (for example `p4 admin checkpoint [-z | -Z]` or
75/// `p4 admin updatespecdepot [-a | -s type]`). Each variant of such a group
76/// implements this trait to inject its own CLI arguments; the selected
77/// variant is encoded in a type parameter so that the alternatives are
78/// unavailable at compile time.
79///
80/// Variants may carry their own data (for example the `type` argument of
81/// `-s type`) and inject any number of arguments, keeping the trait open to
82/// option groups more complex than a single flag.
83pub trait ExclusiveOption {
84    #[allow(unused_variables)]
85    /// Inject the CLI arguments corresponding to this selection into
86    /// `command`.
87    fn inject_args(&self, command: &mut Command) {}
88}
89
90pub trait SubCommand {
91    fn name(&self) -> &str;
92
93    fn inject_local_args(&self, command: &mut Command);
94
95    fn global_opts(&self) -> Option<&GlobalOpts> {
96        None
97    }
98
99    fn inject_args(&self, command: &mut Command) {
100        // inject global opts
101        if let Some(global_opts) = self.global_opts() {
102            global_opts.setup_args(command);
103        };
104        // inject local opts
105        self.inject_local_args(command.arg(self.name()));
106    }
107
108    fn setup_command<S: AsRef<OsStr>>(&self, bin: S) -> Command {
109        let mut cmd = Command::new(bin);
110
111        self.inject_args(&mut cmd);
112        cmd
113    }
114}
115
116/// Test-only helper: collects the arguments assembled on a [`Command`] as
117/// strings for easy comparison.
118#[cfg(test)]
119pub(crate) fn args_of(command: &Command) -> Vec<String> {
120    command
121        .get_args()
122        .map(|arg| arg.to_string_lossy().into_owned())
123        .collect()
124}