Skip to main content

perforce_cli/cmd/
where.rs

1use std::{
2    ffi::OsStr,
3    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 ParameterizedSpawn for Where {
75    type Input<'a> = &'a [&'a OsStr];
76    type Output<'a> = Child;
77    type Error = std::io::Error;
78
79    /// Spawns `p4 where` for the given files as a child process with piped
80    /// standard output and error streams; use the returned [`Child`] handle
81    /// to wait for it or interact with it.
82    ///
83    /// For each file provided as a parameter, a set of mappings is output.
84    fn spawn_with<'a>(&mut self, files: Self::Input<'a>) -> Result<Self::Output<'a>, Self::Error> {
85        self.setup_command(&self.bin)
86            .args(files)
87            .stdout(Stdio::piped())
88            .stderr(Stdio::piped())
89            .spawn()
90    }
91}
92
93impl Where {
94    /// Creates a new `p4 where` command.
95    ///
96    /// `bin` is the path to the Perforce command-line executable.
97    pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
98        Self {
99            bin: bin.into(),
100            global_opts,
101        }
102    }
103
104    /// # Description
105    ///
106    /// g-opts
107    ///
108    #[cfg_attr(
109        feature = "lt2014_2",
110        doc = "See the [Global Options](GlobalOpts) section."
111    )]
112    #[cfg_attr(
113        all(feature = "lt2015_1", not(feature = "lt2014_2")),
114        doc = "See the [“Global Options”](GlobalOpts) section."
115    )]
116    #[cfg_attr(
117        all(feature = "lt2017_1", not(feature = "lt2015_1")),
118        doc = "See [“Global Options”](GlobalOpts)."
119    )]
120    #[cfg_attr(
121        all(feature = "lt2018_2", not(feature = "lt2017_1")),
122        doc = "See [Global Options](GlobalOpts)."
123    )]
124    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
125    pub fn get_global_opts(&self) -> &GlobalOpts {
126        &self.global_opts
127    }
128
129    /// # Description
130    ///
131    /// g-opts
132    ///
133    #[cfg_attr(
134        feature = "lt2014_2",
135        doc = "See the [Global Options](GlobalOpts) section."
136    )]
137    #[cfg_attr(
138        all(feature = "lt2015_1", not(feature = "lt2014_2")),
139        doc = "See the [“Global Options”](GlobalOpts) section."
140    )]
141    #[cfg_attr(
142        all(feature = "lt2017_1", not(feature = "lt2015_1")),
143        doc = "See [“Global Options”](GlobalOpts)."
144    )]
145    #[cfg_attr(
146        all(feature = "lt2018_2", not(feature = "lt2017_1")),
147        doc = "See [Global Options](GlobalOpts)."
148    )]
149    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
150    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
151        self.global_opts = v;
152        self
153    }
154
155    /// # Description
156    ///
157    /// g-opts
158    ///
159    #[cfg_attr(
160        feature = "lt2014_2",
161        doc = "See the [Global Options](GlobalOpts) section."
162    )]
163    #[cfg_attr(
164        all(feature = "lt2015_1", not(feature = "lt2014_2")),
165        doc = "See the [“Global Options”](GlobalOpts) section."
166    )]
167    #[cfg_attr(
168        all(feature = "lt2017_1", not(feature = "lt2015_1")),
169        doc = "See [“Global Options”](GlobalOpts)."
170    )]
171    #[cfg_attr(
172        all(feature = "lt2018_2", not(feature = "lt2017_1")),
173        doc = "See [Global Options](GlobalOpts)."
174    )]
175    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
176    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
177        self.global_opts = v;
178        self
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185    use crate::cmd::args_of;
186
187    /// Dry-run check of the assembled `p4 where` command line; no process is
188    /// spawned.
189    #[test]
190    fn without_options() {
191        let r#where = Where::new("p4", GlobalOpts::new());
192
193        assert_eq!(args_of(&r#where.setup_command("p4")), ["where"]);
194    }
195
196    #[test]
197    fn with_global_opts() {
198        let r#where = Where::new(
199            "p4",
200            GlobalOpts::new().port("localhost:1666").quiet_mode(true),
201        );
202
203        assert_eq!(
204            args_of(&r#where.setup_command("p4")),
205            ["-p", "localhost:1666", "-q", "where"]
206        );
207    }
208
209    #[test]
210    fn with_files() {
211        let r#where = Where::new("p4", GlobalOpts::new());
212
213        assert_eq!(
214            args_of(r#where.setup_command("p4").args(["file.c"])),
215            ["where", "file.c"]
216        );
217    }
218}