Skip to main content

perforce_cli/cmd/
where.rs

1use std::{
2    ffi::OsStr,
3    path::{Path, PathBuf},
4    process::{Child, Command, Stdio},
5};
6
7use super::SubCommand;
8
9use crate::global::GlobalOpts;
10use crate::spawn::ParameterizedSpawn;
11
12#[cfg_attr(feature = "lt2015_1", doc = "`p4 [g-opts] where [file ...]`")]
13#[cfg_attr(
14    all(feature = "lt2016_1", not(feature = "lt2015_1")),
15    doc = "`p4 [g-opts] where [file …]`"
16)]
17#[cfg_attr(not(feature = "lt2016_1"), doc = "`p4 [g-opts] where [file ...]`")]
18///
19#[cfg_attr(
20    feature = "lt2023_2",
21    doc = "Show where a particular file is located, as determined by the client view.",
22    doc = "",
23    doc = "`p4 where` uses the client view and root (as set in `p4 client`) to print files'",
24    doc = "locations relative to the top of the depot, relative to the top of the client",
25    doc = "workspace, and relative to the top of the local OS directory tree. The command",
26    doc = "does not check to see if the file exists; it merely reports where the file",
27    doc = "*would be* located if it *did* exist.",
28    doc = "",
29    doc = "For each file provided as a parameter, a set of mappings is output. Each set",
30    doc = "of mappings is composed of lines consisting of three parts: the first part",
31    doc = "is the filename expressed in depot syntax, the second part is the filename",
32    doc = "expressed in client syntax, and the third is the local OS path of the file."
33)]
34#[cfg_attr(
35    not(feature = "lt2023_2"),
36    doc = "Show how the specified files are mapped by the client view.",
37    doc = "",
38    doc = "`p4 where` uses the client view and root to print files locations relative to",
39    doc = "the top of the depot, relative to the top of the client workspace, and",
40    doc = "relative to the top of the local OS directory tree. The client mappings are",
41    doc = "set by the `p4 client` command. The `p4 where` command does not check to see",
42    doc = "whether the file exists. Instead, it reports where the file *would be*",
43    doc = "located if it *did* exist.",
44    doc = "",
45    doc = "The command accepts wildcards, such as `p4 where *.html`",
46    doc = "",
47    doc = "For each file provided as a parameter, a set of mappings is output. Each set",
48    doc = "of mappings consists of:",
49    doc = "",
50    doc = "- the filename in depot syntax. such as `//depot/project1/my-file.html`",
51    doc = "- the filename in client syntax, such as `//maria/depot/project1/my-file.html`",
52    doc = "- the filename in local syntax, which is the local operating system path,",
53    doc = "such as `C:\\Users\\maria\\project1\\my-file.html`"
54)]
55#[derive(Debug, Clone, Default)]
56pub struct Where {
57    bin: PathBuf,
58
59    global_opts: GlobalOpts,
60}
61
62impl SubCommand for Where {
63    fn name(&self) -> &str {
64        "where"
65    }
66
67    fn inject_local_args(&self, _: &mut Command) {}
68
69    fn global_opts(&self) -> Option<&GlobalOpts> {
70        Some(&self.global_opts)
71    }
72}
73
74impl<S, I> ParameterizedSpawn<(S,)> for Where
75where
76    S: IntoIterator<Item = I>,
77    I: AsRef<OsStr>,
78{
79    type Output = Child;
80    type Error = std::io::Error;
81
82    /// Spawns `p4 where` for the given files as a child process with piped
83    /// standard output and error streams; use the returned [`Child`] handle
84    /// to wait for it or interact with it.
85    ///
86    /// For each file provided as a parameter, a set of mappings is output.
87    fn spawn_with(&mut self, (files,): (S,)) -> Result<Self::Output, Self::Error> {
88        self.setup_command(&self.bin)
89            .args(files)
90            .stdout(Stdio::piped())
91            .stderr(Stdio::piped())
92            .spawn()
93    }
94}
95
96impl Where {
97    /// Get the Perforce command path.
98    pub fn get_bin(&self) -> &Path {
99        &self.bin
100    }
101
102    /// Set the Perforce command path.
103    pub fn set_bin(&mut self, bin: impl Into<PathBuf>) -> &mut Self {
104        self.bin = bin.into();
105        self
106    }
107
108    /// Set the Perforce command path.
109    pub fn bin(mut self, bin: impl Into<PathBuf>) -> Self {
110        self.set_bin(bin);
111        self
112    }
113
114    /// Creates a new `p4 where` command.
115    ///
116    /// `bin` is the path to the Perforce command-line executable.
117    pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
118        Self {
119            bin: bin.into(),
120            global_opts,
121        }
122    }
123
124    /// # Description
125    ///
126    /// g-opts
127    ///
128    #[cfg_attr(
129        feature = "lt2014_2",
130        doc = "See the [Global Options](GlobalOpts) section."
131    )]
132    #[cfg_attr(
133        all(feature = "lt2015_1", not(feature = "lt2014_2")),
134        doc = "See the [“Global Options”](GlobalOpts) section."
135    )]
136    #[cfg_attr(
137        all(feature = "lt2017_1", not(feature = "lt2015_1")),
138        doc = "See [“Global Options”](GlobalOpts)."
139    )]
140    #[cfg_attr(
141        all(feature = "lt2018_2", not(feature = "lt2017_1")),
142        doc = "See [Global Options](GlobalOpts)."
143    )]
144    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
145    pub fn get_global_opts(&self) -> &GlobalOpts {
146        &self.global_opts
147    }
148
149    /// # Description
150    ///
151    /// g-opts
152    ///
153    #[cfg_attr(
154        feature = "lt2014_2",
155        doc = "See the [Global Options](GlobalOpts) section."
156    )]
157    #[cfg_attr(
158        all(feature = "lt2015_1", not(feature = "lt2014_2")),
159        doc = "See the [“Global Options”](GlobalOpts) section."
160    )]
161    #[cfg_attr(
162        all(feature = "lt2017_1", not(feature = "lt2015_1")),
163        doc = "See [“Global Options”](GlobalOpts)."
164    )]
165    #[cfg_attr(
166        all(feature = "lt2018_2", not(feature = "lt2017_1")),
167        doc = "See [Global Options](GlobalOpts)."
168    )]
169    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
170    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
171        self.global_opts = v;
172        self
173    }
174
175    /// # Description
176    ///
177    /// g-opts
178    ///
179    #[cfg_attr(
180        feature = "lt2014_2",
181        doc = "See the [Global Options](GlobalOpts) section."
182    )]
183    #[cfg_attr(
184        all(feature = "lt2015_1", not(feature = "lt2014_2")),
185        doc = "See the [“Global Options”](GlobalOpts) section."
186    )]
187    #[cfg_attr(
188        all(feature = "lt2017_1", not(feature = "lt2015_1")),
189        doc = "See [“Global Options”](GlobalOpts)."
190    )]
191    #[cfg_attr(
192        all(feature = "lt2018_2", not(feature = "lt2017_1")),
193        doc = "See [Global Options](GlobalOpts)."
194    )]
195    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
196    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
197        self.global_opts = v;
198        self
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use crate::cmd::args_of;
206
207    #[test]
208    fn bin_accessors_update_executable_path() {
209        let mut command = Where::new("p4", GlobalOpts::new());
210        assert_eq!(command.get_bin(), Path::new("p4"));
211
212        command.set_bin("/usr/local/bin/p4");
213        assert_eq!(command.get_bin(), Path::new("/usr/local/bin/p4"));
214
215        let command = command.bin("p4.exe");
216        assert_eq!(command.get_bin(), Path::new("p4.exe"));
217    }
218
219    /// Dry-run check of the assembled `p4 where` command line; no process is
220    /// spawned.
221    #[test]
222    fn without_options() {
223        let r#where = Where::new("p4", GlobalOpts::new());
224
225        assert_eq!(args_of(&r#where.setup_command("p4")), ["where"]);
226    }
227
228    #[test]
229    fn with_global_opts() {
230        let r#where = Where::new(
231            "p4",
232            GlobalOpts::new().port("localhost:1666").quiet_mode(true),
233        );
234
235        assert_eq!(
236            args_of(&r#where.setup_command("p4")),
237            ["-p", "localhost:1666", "-q", "where"]
238        );
239    }
240
241    #[test]
242    fn with_files() {
243        let r#where = Where::new("p4", GlobalOpts::new());
244
245        assert_eq!(
246            args_of(r#where.setup_command("p4").args(["file.c"])),
247            ["where", "file.c"]
248        );
249    }
250}