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#[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 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 pub fn get_bin(&self) -> &Path {
99 &self.bin
100 }
101
102 pub fn set_bin(&mut self, bin: impl Into<PathBuf>) -> &mut Self {
104 self.bin = bin.into();
105 self
106 }
107
108 pub fn bin(mut self, bin: impl Into<PathBuf>) -> Self {
110 self.set_bin(bin);
111 self
112 }
113
114 pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
118 Self {
119 bin: bin.into(),
120 global_opts,
121 }
122 }
123
124 #[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 #[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 #[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 #[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}