p4_cmd/
files.rs

1use std::vec;
2
3use error;
4use p4;
5
6/// List files in the depot.
7///
8/// List details about specified files: depot file name, revision,
9/// file, type, change action and changelist number of the current
10/// head revision. If client syntax is used to specify the file
11/// argument, the client view mapping is used to determine the
12/// corresponding depot files.
13///
14/// By default, the head revision is listed.  If the file argument
15/// specifies a revision, then all files at that revision are listed.
16/// If the file argument specifies a revision range, the highest revision
17/// in the range is used for each file. For details about specifying
18/// revisions, see 'p4 help revisions'.
19///
20/// # Examples
21///
22/// ```rust,no_run
23/// let p4 = p4_cmd::P4::new();
24/// let files = p4.files("//depot/dir/*").run().unwrap();
25/// for file in files {
26///     println!("{:?}", file);
27/// }
28/// ```
29#[derive(Debug, Clone)]
30pub struct FilesCommand<'p, 'f> {
31    connection: &'p p4::P4,
32    file: Vec<&'f str>,
33
34    list_revisions: bool,
35    syncable_only: bool,
36    ignore_case: bool,
37    max: Option<usize>,
38}
39
40impl<'p, 'f> FilesCommand<'p, 'f> {
41    pub fn new(connection: &'p p4::P4, file: &'f str) -> Self {
42        Self {
43            connection,
44            file: vec![file],
45            list_revisions: false,
46            syncable_only: false,
47            ignore_case: false,
48            max: None,
49        }
50    }
51
52    pub fn file(mut self, file: &'f str) -> Self {
53        self.file.push(file);
54        self
55    }
56
57    /// The -a flag displays all revisions within the specific range, rather
58    /// than just the highest revision in the range.
59    pub fn list_revisions(mut self, list_revisions: bool) -> Self {
60        self.list_revisions = list_revisions;
61        self
62    }
63
64    /// The -e flag displays files with an action of anything other than
65    /// deleted, purged or archived.  Typically this revision is always
66    /// available to sync or integrate from.
67    pub fn syncable_only(mut self, syncable_only: bool) -> Self {
68        self.syncable_only = syncable_only;
69        self
70    }
71
72    /// The -i flag is used to ignore the case of the file argument when
73    /// listing files in a case sensitive server.
74    pub fn ignore_case(mut self, ignore_case: bool) -> Self {
75        self.ignore_case = ignore_case;
76        self
77    }
78
79    /// The -m flag limits files to the first 'max' number of files.
80    pub fn set_max(mut self, max: Option<usize>) -> Self {
81        self.max = max;
82        self
83    }
84
85    /// Run the `files` command.
86    pub fn run(self) -> Result<Files, error::P4Error> {
87        let mut cmd = self.connection.connect_with_retries(None);
88        cmd.arg("files");
89        if self.list_revisions {
90            cmd.arg("-a");
91        }
92        if self.syncable_only {
93            cmd.arg("-e");
94        }
95        if self.ignore_case {
96            cmd.arg("-i");
97        }
98        if let Some(max) = self.max {
99            cmd.arg(format!("-m {}", max));
100        }
101        for file in self.file {
102            cmd.arg(file);
103        }
104        let data = cmd.output().map_err(|e| {
105            error::ErrorKind::SpawnFailed
106                .error()
107                .set_cause(e)
108                .set_context(format!("Command: {:?}", cmd))
109        })?;
110        let (_remains, (mut items, exit)) = files_parser::files(&data.stdout).map_err(|_| {
111            error::ErrorKind::ParseFailed
112                .error()
113                .set_context(format!("Command: {:?}", cmd))
114        })?;
115        items.push(exit);
116        Ok(Files(items))
117    }
118}
119
120pub type FileItem = error::Item<File>;
121
122pub struct Files(Vec<FileItem>);
123
124impl IntoIterator for Files {
125    type Item = FileItem;
126    type IntoIter = FilesIntoIter;
127
128    fn into_iter(self) -> FilesIntoIter {
129        FilesIntoIter(self.0.into_iter())
130    }
131}
132
133#[derive(Debug)]
134pub struct FilesIntoIter(vec::IntoIter<FileItem>);
135
136impl Iterator for FilesIntoIter {
137    type Item = FileItem;
138
139    #[inline]
140    fn next(&mut self) -> Option<FileItem> {
141        self.0.next()
142    }
143
144    #[inline]
145    fn size_hint(&self) -> (usize, Option<usize>) {
146        self.0.size_hint()
147    }
148
149    #[inline]
150    fn count(self) -> usize {
151        self.0.count()
152    }
153}
154
155#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct File {
157    pub depot_file: String,
158    pub rev: usize,
159    pub change: usize,
160    pub action: p4::Action,
161    pub file_type: p4::FileType,
162    pub time: p4::Time,
163    non_exhaustive: (),
164}
165
166mod files_parser {
167    use super::*;
168
169    use super::super::parser::*;
170
171    named!(file<&[u8], File>,
172        do_parse!(
173            depot_file: depot_file >>
174            rev: rev >>
175            change: change >>
176            action: action >>
177            file_type: file_type >>
178            time: time >>
179            (
180                File {
181                    depot_file: depot_file.path.to_owned(),
182                    rev: rev.rev,
183                    change: change.change,
184                    action: action.action.parse().expect("Unknown to capture all"),
185                    file_type: file_type.ft.parse().expect("Unknown to capture all"),
186                    time: p4::from_timestamp(time.time),
187                    non_exhaustive: (),
188                }
189            )
190        )
191    );
192
193    named!(item<&[u8], FileItem>,
194        alt!(
195            map!(file, data_to_item) |
196            map!(error, error_to_item) |
197            map!(info, info_to_item)
198        )
199    );
200
201    named!(pub files<&[u8], (Vec<FileItem>, FileItem)>,
202        pair!(
203            many0!(item),
204            map!(exit, exit_to_item)
205        )
206    );
207}