use std::path;
use std::vec;
use error;
use p4;
#[derive(Debug, Clone)]
pub struct SyncCommand<'p, 'f> {
connection: &'p p4::P4,
file: Vec<&'f str>,
force: bool,
preview: bool,
server_only: bool,
client_only: bool,
verify: bool,
max_files: Option<usize>,
parallel: Option<usize>,
}
impl<'p, 'f> SyncCommand<'p, 'f> {
pub fn new(connection: &'p p4::P4, file: &'f str) -> Self {
Self {
connection: connection,
file: vec![file],
force: false,
preview: false,
server_only: false,
client_only: false,
verify: false,
max_files: None,
parallel: None,
}
}
pub fn file(mut self, dir: &'f str) -> Self {
self.file.push(dir);
self
}
pub fn force(mut self, force: bool) -> Self {
self.force = force;
self
}
pub fn preview(mut self, preview: bool) -> Self {
self.preview = preview;
self
}
pub fn server_only(mut self, server_only: bool) -> Self {
self.server_only = server_only;
self
}
pub fn client_only(mut self, client_only: bool) -> Self {
self.client_only = client_only;
self
}
pub fn verify(mut self, verify: bool) -> Self {
self.verify = verify;
self
}
pub fn max_files(mut self, max_files: usize) -> Self {
self.max_files = Some(max_files);
self
}
pub fn parallel(mut self, parallel: usize) -> Self {
self.parallel = Some(parallel);
self
}
pub fn run(self) -> Result<Files, error::P4Error> {
let mut cmd = self.connection.connect_with_retries(None);
cmd.arg("sync");
if self.force {
cmd.arg("-f");
}
if self.preview {
cmd.arg("-n");
}
if self.server_only {
cmd.arg("-k");
}
if self.client_only {
cmd.arg("-p");
}
if self.verify {
cmd.arg("-s");
}
if let Some(max_files) = self.max_files {
let max_files = format!("{}", max_files);
cmd.args(&["-m", &max_files]);
}
if let Some(parallel) = self.parallel {
let parallel = format!("{}", parallel);
cmd.args(&["--parallel", ¶llel]);
}
for file in self.file {
cmd.arg(file);
}
let data = cmd.output().map_err(|e| {
error::ErrorKind::SpawnFailed
.error()
.set_cause(e)
.set_context(format!("Command: {:?}", cmd))
})?;
let (_remains, (mut items, exit)) = files_parser::files(&data.stdout).map_err(|_| {
error::ErrorKind::ParseFailed
.error()
.set_context(format!("Command: {:?}", cmd))
})?;
items.push(exit);
Ok(Files(items))
}
}
pub type FileItem = error::Item<File>;
pub struct Files(Vec<FileItem>);
impl IntoIterator for Files {
type Item = FileItem;
type IntoIter = FilesIntoIter;
fn into_iter(self) -> FilesIntoIter {
FilesIntoIter(self.0.into_iter())
}
}
#[derive(Debug)]
pub struct FilesIntoIter(vec::IntoIter<FileItem>);
impl Iterator for FilesIntoIter {
type Item = FileItem;
#[inline]
fn next(&mut self) -> Option<FileItem> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn count(self) -> usize {
self.0.count()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FileContent {
#[doc(hidden)]
__Nonexhaustive,
Text(Vec<String>),
Binary(Vec<u8>),
}
impl FileContent {
pub fn as_text(&self) -> Option<&[String]> {
match self {
FileContent::Text(c) => Some(&c),
_ => None,
}
}
pub fn as_binary(&self) -> Option<&[u8]> {
match self {
FileContent::Binary(c) => Some(&c),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct File {
pub depot_file: String,
pub client_file: path::PathBuf,
pub rev: usize,
pub action: p4::Action,
pub file_size: usize,
non_exhaustive: (),
}
mod files_parser {
use super::*;
use super::super::parser::*;
named!(pub file<&[u8], File>,
do_parse!(
depot_file: depot_file >>
client_file: client_file >>
rev: rev >>
action: action >>
file_size: file_size >>
_ignore: opt!(delimited!(ignore_info1, ignore_info1, change)) >>
(
File {
depot_file: depot_file.path.to_owned(),
client_file: path::PathBuf::from(client_file.path),
rev: rev.rev,
action: action.action.parse().expect("`Unknown` to capture all"),
file_size: file_size.size,
non_exhaustive: (),
}
)
)
);
named!(item<&[u8], FileItem>,
alt!(
map!(file, data_to_item) |
map!(error, error_to_item) |
map!(info, info_to_item)
)
);
named!(pub files<&[u8], (Vec<FileItem>, FileItem)>,
pair!(
many0!(item),
map!(exit, exit_to_item)
)
);
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn sync_single() {
let output: &[u8] = br#"info1: depotFile //depot/dir/file
info1: clientFile /home/user/depot/dir/file
info1: rev 1
info1: action added
info1: fileSize 1016
info1: totalFileSize 865153
info1: totalFileCount 24
info1: change 25662947
exit: 0
"#;
let (_remains, (items, exit)) = files_parser::files(output).unwrap();
let first = items[0].as_data().unwrap();
assert_eq!(first.depot_file, "//depot/dir/file");
assert_eq!(exit.as_error(), Some(&error::OperationError::new(0)));
}
#[test]
fn sync_multi() {
let output: &[u8] = br#"info1: depotFile //depot/dir/file
info1: clientFile /home/user/depot/dir/file
info1: rev 1
info1: action added
info1: fileSize 1016
info1: totalFileSize 865153
info1: totalFileCount 24
info1: change 25662947
info1: depotFile //depot/dir/file1
info1: clientFile /home/user/depot/dir/file1
info1: rev 1
info1: action added
info1: fileSize 729154
exit: 0
"#;
let (_remains, (items, exit)) = files_parser::files(output).unwrap();
let first = items[0].as_data().unwrap();
let last = items[1].as_data().unwrap();
assert_eq!(first.depot_file, "//depot/dir/file");
assert_eq!(last.depot_file, "//depot/dir/file1");
assert_eq!(exit.as_error(), Some(&error::OperationError::new(0)));
}
}