use std::fmt;
use std::path;
use std::process;
use std::str;
use chrono;
use chrono::TimeZone;
use dirs;
use files;
use print;
use sync;
use where_;
#[derive(Clone, Debug)]
pub struct P4 {
custom_p4: Option<path::PathBuf>,
port: Option<String>,
user: Option<String>,
password: Option<String>,
client: Option<String>,
retries: Option<usize>,
}
impl P4 {
pub fn new() -> Self {
Self {
custom_p4: None,
port: None,
user: None,
password: None,
client: None,
retries: None,
}
}
pub fn set_p4_cmd(mut self, custom_p4: Option<path::PathBuf>) -> Self {
self.custom_p4 = custom_p4;
self
}
pub fn set_port(mut self, port: Option<String>) -> Self {
self.port = port;
self
}
pub fn set_user(mut self, user: Option<String>) -> Self {
self.user = user;
self
}
pub fn set_password(mut self, password: Option<String>) -> Self {
self.password = password;
self
}
pub fn set_client(mut self, client: Option<String>) -> Self {
self.client = client;
self
}
pub fn set_retries(mut self, retries: Option<usize>) -> Self {
self.retries = retries;
self
}
pub fn print<'p, 'f>(&'p self, file: &'f str) -> print::PrintCommand<'p, 'f> {
print::PrintCommand::new(self, file)
}
pub fn sync<'p, 'f>(&'p self, file: &'f str) -> sync::SyncCommand<'p, 'f> {
sync::SyncCommand::new(self, file)
}
pub fn files<'p, 'f>(&'p self, file: &'f str) -> files::FilesCommand<'p, 'f> {
files::FilesCommand::new(self, file)
}
pub fn dirs<'p, 'f, 's>(&'p self, dir: &'f str) -> dirs::DirsCommand<'p, 'f, 's> {
dirs::DirsCommand::new(self, dir)
}
pub fn where_<'p, 'f>(&'p self) -> where_::WhereCommand<'p, 'f> {
where_::WhereCommand::new(self)
}
pub(crate) fn connect(&self) -> process::Command {
let p4_cmd = self
.custom_p4
.as_ref()
.map(path::PathBuf::as_path)
.unwrap_or_else(|| path::Path::new("p4"));
let mut cmd = process::Command::new(p4_cmd);
cmd.args(&["-Gs", "-C utf8"]);
if let Some(ref port) = self.port {
cmd.args(&["-p", port.as_str()]);
}
if let Some(ref user) = self.user {
cmd.args(&["-u", user.as_str()]);
}
if let Some(ref password) = self.password {
cmd.args(&["-P", password.as_str()]);
}
if let Some(ref client) = self.client {
cmd.args(&["-c", client.as_str()]);
}
cmd
}
pub(crate) fn connect_with_retries(&self, retries: Option<usize>) -> process::Command {
let mut cmd = self.connect();
if let Some(retries) = retries.or(self.retries) {
let retries = format!("{}", retries);
cmd.args(&["-r", &retries]);
}
cmd
}
}
pub type Time = chrono::DateTime<chrono::Utc>;
#[allow(dead_code)]
pub(crate) fn to_timestamp(time: &Time) -> i64 {
time.timestamp()
}
pub(crate) fn from_timestamp(timestamp: i64) -> Time {
chrono::Utc.timestamp(timestamp, 0)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Action {
#[doc(hidden)]
__Nonexhaustive,
Add,
Edit,
Delete,
Branch,
MoveAdd,
MoveDelete,
Integrate,
Import,
Purge,
Archive,
Unknown(String),
}
impl str::FromStr for Action {
type Err = fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let ft = match s {
"add" => Action::Add,
"edit" => Action::Edit,
"delete" => Action::Delete,
"branch" => Action::Branch,
"move/add" => Action::MoveAdd,
"move/delete" => Action::MoveDelete,
"integrate" => Action::Integrate,
"import" => Action::Import,
"purge" => Action::Purge,
"archive" => Action::Archive,
s => Action::Unknown(s.to_owned()),
};
Ok(ft)
}
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = match self {
Action::Add => "add",
Action::Edit => "edit",
Action::Delete => "delete",
Action::Branch => "branch",
Action::MoveAdd => "move/add",
Action::MoveDelete => "move/delete",
Action::Integrate => "integrate",
Action::Import => "import",
Action::Purge => "purge",
Action::Archive => "archive",
Action::Unknown(ref s) => s.as_str(),
Action::__Nonexhaustive => unreachable!("This is a private variant"),
};
write!(f, "{}", value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BaseFileType {
#[doc(hidden)]
__Nonexhaustive,
Text,
Binary,
Symlink,
Unicode,
Utf8,
Utf16,
Unknown(String),
}
impl Default for BaseFileType {
fn default() -> Self {
BaseFileType::Text
}
}
impl str::FromStr for BaseFileType {
type Err = fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let ft = match s {
"text" => BaseFileType::Text,
"binary" => BaseFileType::Binary,
"symlink" => BaseFileType::Symlink,
"unicode" => BaseFileType::Unicode,
"utf8" => BaseFileType::Utf8,
"utf16" => BaseFileType::Utf16,
s => BaseFileType::Unknown(s.to_owned()),
};
Ok(ft)
}
}
impl fmt::Display for BaseFileType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value = match self {
BaseFileType::Text => "text",
BaseFileType::Binary => "binary",
BaseFileType::Symlink => "symlink",
BaseFileType::Unicode => "unicode",
BaseFileType::Utf8 => "utf8",
BaseFileType::Utf16 => "utf16",
BaseFileType::Unknown(ref s) => s.as_str(),
BaseFileType::__Nonexhaustive => unreachable!("This is a private variant"),
};
write!(f, "{}", value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct FileTypeModifiers {
pub always_writeable: bool,
pub executable: bool,
pub rcs_expansion: bool,
pub exclusive: bool,
pub full: bool,
pub deltas: bool,
pub full_uncompressed: bool,
pub head: bool,
pub revisions: Option<usize>,
pub modtime: bool,
pub archive: bool,
non_exhaustive: (),
}
impl FileTypeModifiers {
pub fn new() -> Self {
Default::default()
}
}
impl str::FromStr for FileTypeModifiers {
type Err = fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut modifiers = FileTypeModifiers::default();
for flag in s.chars() {
match flag {
'w' => modifiers.always_writeable = true,
'x' => modifiers.executable = true,
'k' => modifiers.rcs_expansion = true,
'l' => modifiers.exclusive = true,
'C' => modifiers.full = true,
'D' => modifiers.deltas = true,
'F' => modifiers.full_uncompressed = true,
'S' => modifiers.head = true,
'm' => modifiers.modtime = true,
'X' => modifiers.archive = true,
_ => return Err(fmt::Error),
}
}
Ok(modifiers)
}
}
impl fmt::Display for FileTypeModifiers {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.always_writeable {
write!(f, "w")?;
}
if self.executable {
write!(f, "x")?;
}
if self.rcs_expansion {
write!(f, "k")?;
}
if self.exclusive {
write!(f, "l")?;
}
if self.full {
write!(f, "C")?;
}
if self.deltas {
write!(f, "D")?;
}
if self.full_uncompressed {
write!(f, "S")?;
}
if self.head {
write!(f, "S")?;
}
if let Some(revisions) = self.revisions {
write!(f, "S{}", revisions)?;
}
if self.modtime {
write!(f, "m")?;
}
if self.archive {
write!(f, "X")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct FileType {
pub base: BaseFileType,
pub modifiers: Option<FileTypeModifiers>,
non_exhaustive: (),
}
impl FileType {
pub fn new() -> Self {
Default::default()
}
pub fn base(mut self, base: BaseFileType) -> Self {
self.base = base;
self
}
pub fn modifiers(mut self, modifiers: Option<FileTypeModifiers>) -> Self {
self.modifiers = modifiers;
self
}
}
impl str::FromStr for FileType {
type Err = fmt::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut itr = s.splitn(2, '+');
let base = itr.next().ok_or(fmt::Error)?;
let base = base.parse().map_err(|_| fmt::Error)?;
let modifiers = itr
.next()
.map(|f| {
let modifiers: FileTypeModifiers = f.parse()?;
Ok(modifiers)
}).map_or(Ok(None), |r| r.map(Some))?;
let ft = FileType {
base,
modifiers,
non_exhaustive: (),
};
Ok(ft)
}
}
impl fmt::Display for FileType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.base)?;
if let Some(ref modifiers) = self.modifiers {
write!(f, "+{}", modifiers)?;
}
Ok(())
}
}