p4_cmd/
dirs.rs

1use std::vec;
2
3use error;
4use p4;
5
6/// List depot subdirectories
7///
8/// List directories that match the specified file pattern (dir).
9/// This command does not support the recursive wildcard (...).
10/// Use the * wildcard instead.
11///
12/// Perforce does not track directories individually. A path is treated
13/// as a directory if there are any undeleted files with that path as a
14/// prefix.
15///
16/// By default, all directories containing files are listed. If the dir
17/// argument includes a revision range, only directories containing files
18/// in the range are listed. For details about specifying file revisions,
19/// see 'p4 help revisions'.
20///
21/// # Examples
22///
23/// ```rust,no_run
24/// let p4 = p4_cmd::P4::new();
25/// let dirs = p4.dirs("//depot/dir/*").run().unwrap();
26/// for dir in dirs {
27///     println!("{:?}", dir);
28/// }
29/// ```
30#[derive(Debug, Clone)]
31pub struct DirsCommand<'p, 'f, 's> {
32    connection: &'p p4::P4,
33    dir: Vec<&'f str>,
34
35    client_only: bool,
36    stream: Option<&'s str>,
37    include_deleted: bool,
38    include_synced: bool,
39    ignore_case: bool,
40}
41
42impl<'p, 'f, 's> DirsCommand<'p, 'f, 's> {
43    pub fn new(connection: &'p p4::P4, dir: &'f str) -> Self {
44        Self {
45            connection,
46            dir: vec![dir],
47            client_only: false,
48            stream: None,
49            include_deleted: false,
50            include_synced: false,
51            ignore_case: false,
52        }
53    }
54
55    pub fn dir(mut self, dir: &'f str) -> Self {
56        self.dir.push(dir);
57        self
58    }
59
60    /// The -C flag lists only directories that fall within the current
61    /// client view.
62    pub fn client_only(mut self, client_only: bool) -> Self {
63        self.client_only = client_only;
64        self
65    }
66
67    /// The -S flag limits output to depot directories mapped in a stream's
68    /// client view.
69    pub fn set_stream(mut self, stream: &'s str) -> Self {
70        self.stream = Some(stream);
71        self
72    }
73
74    /// The -D flag includes directories containing only deleted files.
75    pub fn include_deleted(mut self, include_deleted: bool) -> Self {
76        self.include_deleted = include_deleted;
77        self
78    }
79
80    /// The -H flag lists directories containing files synced to the current
81    /// client workspace.
82    pub fn include_synced(mut self, include_synced: bool) -> Self {
83        self.include_synced = include_synced;
84        self
85    }
86
87    /// The -i flag is used to ignore the case of the file pattern when
88    /// listing directories in a case sensitive server. This flag is not
89    /// compatible with the -C option.
90    pub fn ignore_case(mut self, ignore_case: bool) -> Self {
91        self.ignore_case = ignore_case;
92        self
93    }
94
95    /// Run the `dirs` command.
96    pub fn run(self) -> Result<Dirs, error::P4Error> {
97        let mut cmd = self.connection.connect_with_retries(None);
98        cmd.arg("dirs");
99        if self.client_only {
100            cmd.arg("-C");
101        }
102        if let Some(stream) = self.stream {
103            cmd.args(&["-S", stream]);
104        }
105        if self.include_deleted {
106            cmd.arg("-D");
107        }
108        if self.include_synced {
109            cmd.arg("-H");
110        }
111        if self.ignore_case {
112            cmd.arg("-i");
113        }
114        for dir in self.dir {
115            cmd.arg(dir);
116        }
117        let data = cmd.output().map_err(|e| {
118            error::ErrorKind::SpawnFailed
119                .error()
120                .set_cause(e)
121                .set_context(format!("Command: {:?}", cmd))
122        })?;
123        let (_remains, (mut items, exit)) = dirs_parser::dirs(&data.stdout).map_err(|_| {
124            error::ErrorKind::ParseFailed
125                .error()
126                .set_context(format!("Command: {:?}", cmd))
127        })?;
128        items.push(exit);
129        Ok(Dirs(items))
130    }
131}
132
133pub type DirItem = error::Item<Dir>;
134
135pub struct Dirs(Vec<DirItem>);
136
137impl IntoIterator for Dirs {
138    type Item = DirItem;
139    type IntoIter = DirsIntoIter;
140
141    fn into_iter(self) -> DirsIntoIter {
142        DirsIntoIter(self.0.into_iter())
143    }
144}
145
146#[derive(Debug)]
147pub struct DirsIntoIter(vec::IntoIter<DirItem>);
148
149impl Iterator for DirsIntoIter {
150    type Item = DirItem;
151
152    #[inline]
153    fn next(&mut self) -> Option<DirItem> {
154        self.0.next()
155    }
156
157    #[inline]
158    fn size_hint(&self) -> (usize, Option<usize>) {
159        self.0.size_hint()
160    }
161
162    #[inline]
163    fn count(self) -> usize {
164        self.0.count()
165    }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq)]
169pub struct Dir {
170    pub dir: String,
171    non_exhaustive: (),
172}
173
174mod dirs_parser {
175    use super::super::parser::*;
176
177    named!(dir_<&[u8], super::Dir>,
178        do_parse!(
179            dir: dir >>
180            (
181                super::Dir {
182                    dir: dir.dir.to_owned(),
183                    non_exhaustive: (),
184                }
185            )
186        )
187    );
188
189    named!(item<&[u8], super::DirItem>,
190        alt!(
191            map!(dir_, data_to_item) |
192            map!(error, error_to_item) |
193            map!(info, info_to_item)
194        )
195    );
196
197    named!(pub dirs<&[u8], (Vec<super::DirItem>, super::DirItem)>,
198        pair!(
199            many0!(item),
200            map!(exit, exit_to_item)
201        )
202    );
203}