use std::{
ffi::OsStr,
path::PathBuf,
process::{Child, Command, Stdio},
};
use super::{ExclusiveOption, SubCommand, Unselected};
use crate::global::GlobalOpts;
use crate::spawn::ParameterizedSpawn;
#[derive(Debug, Clone, Copy, Default)]
pub struct FullDescription;
impl ExclusiveOption for FullDescription {
fn inject_args(&self, command: &mut Command) {
command.arg("-l");
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct TruncatedDescription;
impl ExclusiveOption for TruncatedDescription {
fn inject_args(&self, command: &mut Command) {
command.arg("-L");
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DisplayContentHistory {
skip_promoted_tasks: bool,
}
impl ExclusiveOption for DisplayContentHistory {
fn inject_args(&self, command: &mut Command) {
command.arg("-h");
if self.skip_promoted_tasks {
command.arg("-p");
}
}
}
#[derive(Debug, Clone, Default)]
pub struct FileLog<L = Unselected, H = Unselected> {
bin: PathBuf,
global_opts: GlobalOpts,
changelist: Option<String>,
content_history: H,
follow_branches: bool,
long_output: L,
limit: Option<u64>,
ignore_non_contributory: bool,
include_time: bool,
}
impl FileLog<Unselected, Unselected> {
pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
Self {
bin: bin.into(),
global_opts,
changelist: None,
content_history: Unselected,
follow_branches: false,
long_output: Unselected,
limit: None,
ignore_non_contributory: false,
include_time: false,
}
}
}
impl<H: ExclusiveOption> FileLog<Unselected, H> {
pub fn full_description(self) -> FileLog<FullDescription, H> {
FileLog {
bin: self.bin,
global_opts: self.global_opts,
changelist: self.changelist,
content_history: self.content_history,
follow_branches: self.follow_branches,
long_output: FullDescription,
limit: self.limit,
ignore_non_contributory: self.ignore_non_contributory,
include_time: self.include_time,
}
}
pub fn truncated_description(self) -> FileLog<TruncatedDescription, H> {
FileLog {
bin: self.bin,
global_opts: self.global_opts,
changelist: self.changelist,
content_history: self.content_history,
follow_branches: self.follow_branches,
long_output: TruncatedDescription,
limit: self.limit,
ignore_non_contributory: self.ignore_non_contributory,
include_time: self.include_time,
}
}
}
impl<L: ExclusiveOption> FileLog<L, Unselected> {
pub fn content_history(self) -> FileLog<L, DisplayContentHistory> {
FileLog {
bin: self.bin,
global_opts: self.global_opts,
changelist: self.changelist,
content_history: DisplayContentHistory {
skip_promoted_tasks: false,
},
follow_branches: self.follow_branches,
long_output: self.long_output,
limit: self.limit,
ignore_non_contributory: self.ignore_non_contributory,
include_time: self.include_time,
}
}
}
impl<L: ExclusiveOption, H: ExclusiveOption> ParameterizedSpawn for FileLog<L, H> {
type Input<'a> = &'a [&'a OsStr];
type Output<'a> = Child;
type Error = std::io::Error;
fn spawn_with<'a>(&mut self, files: Self::Input<'a>) -> Result<Self::Output<'a>, Self::Error> {
self.setup_command(&self.bin)
.args(files)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
}
}
impl<L: ExclusiveOption, H: ExclusiveOption> FileLog<L, H> {
#[cfg_attr(
feature = "lt2014_2",
doc = "See the [Global Options](GlobalOpts) section."
)]
#[cfg_attr(
all(feature = "lt2015_1", not(feature = "lt2014_2")),
doc = "See the [“Global Options”](GlobalOpts) section."
)]
#[cfg_attr(
all(feature = "lt2017_1", not(feature = "lt2015_1")),
doc = "See [“Global Options”](GlobalOpts)."
)]
#[cfg_attr(
all(feature = "lt2018_2", not(feature = "lt2017_1")),
doc = "See [Global Options](GlobalOpts)."
)]
#[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
pub fn get_global_opts(&self) -> &GlobalOpts {
&self.global_opts
}
#[cfg_attr(
feature = "lt2014_2",
doc = "See the [Global Options](GlobalOpts) section."
)]
#[cfg_attr(
all(feature = "lt2015_1", not(feature = "lt2014_2")),
doc = "See the [“Global Options”](GlobalOpts) section."
)]
#[cfg_attr(
all(feature = "lt2017_1", not(feature = "lt2015_1")),
doc = "See [“Global Options”](GlobalOpts)."
)]
#[cfg_attr(
all(feature = "lt2018_2", not(feature = "lt2017_1")),
doc = "See [Global Options](GlobalOpts)."
)]
#[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
self.global_opts = v;
self
}
#[cfg_attr(
feature = "lt2014_2",
doc = "See the [Global Options](GlobalOpts) section."
)]
#[cfg_attr(
all(feature = "lt2015_1", not(feature = "lt2014_2")),
doc = "See the [“Global Options”](GlobalOpts) section."
)]
#[cfg_attr(
all(feature = "lt2017_1", not(feature = "lt2015_1")),
doc = "See [“Global Options”](GlobalOpts)."
)]
#[cfg_attr(
all(feature = "lt2018_2", not(feature = "lt2017_1")),
doc = "See [Global Options](GlobalOpts)."
)]
#[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
pub fn global_opts(mut self, v: GlobalOpts) -> Self {
self.global_opts = v;
self
}
pub fn get_changelist(&self) -> Option<&String> {
self.changelist.as_ref()
}
pub fn set_changelist(&mut self, v: impl Into<String>) -> &mut Self {
self.changelist = Some(v.into());
self
}
pub fn changelist(mut self, v: impl Into<String>) -> Self {
self.changelist = Some(v.into());
self
}
pub fn get_follow_branches(&self) -> bool {
self.follow_branches
}
pub fn set_follow_branches(&mut self, v: bool) -> &mut Self {
self.follow_branches = v;
self
}
pub fn follow_branches(mut self, v: bool) -> Self {
self.follow_branches = v;
self
}
pub fn get_limit(&self) -> Option<u64> {
self.limit
}
pub fn set_limit(&mut self, v: u64) -> &mut Self {
self.limit = Some(v);
self
}
pub fn limit(mut self, v: u64) -> Self {
self.limit = Some(v);
self
}
pub fn get_ignore_non_contributory(&self) -> bool {
self.ignore_non_contributory
}
pub fn set_ignore_non_contributory(&mut self, v: bool) -> &mut Self {
self.ignore_non_contributory = v;
self
}
pub fn ignore_non_contributory(mut self, v: bool) -> Self {
self.ignore_non_contributory = v;
self
}
pub fn get_include_time(&self) -> bool {
self.include_time
}
pub fn set_include_time(&mut self, v: bool) -> &mut Self {
self.include_time = v;
self
}
pub fn include_time(mut self, v: bool) -> Self {
self.include_time = v;
self
}
}
impl<L: ExclusiveOption> FileLog<L, DisplayContentHistory> {
pub fn get_skip_promoted_tasks(&self) -> bool {
self.content_history.skip_promoted_tasks
}
pub fn set_skip_promoted_tasks(&mut self, v: bool) -> &mut Self {
self.content_history.skip_promoted_tasks = v;
self
}
pub fn skip_promoted_tasks(mut self, v: bool) -> Self {
self.content_history.skip_promoted_tasks = v;
self
}
}
impl<L: ExclusiveOption, H: ExclusiveOption> SubCommand for FileLog<L, H> {
fn name(&self) -> &str {
"filelog"
}
fn inject_local_args(&self, command: &mut Command) {
if let Some(changelist) = &self.changelist {
command.arg("-c").arg(changelist);
}
self.content_history.inject_args(command);
if self.follow_branches {
command.arg("-i");
}
self.long_output.inject_args(command);
if let Some(max) = self.limit {
command.arg("-m").arg(max.to_string());
}
if self.ignore_non_contributory {
command.arg("-s");
}
if self.include_time {
command.arg("-t");
}
}
fn global_opts(&self) -> Option<&GlobalOpts> {
Some(&self.global_opts)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cmd::args_of;
#[test]
fn with_files() {
let filelog = FileLog::new("p4", GlobalOpts::default());
let mut cmd = filelog.setup_command("p4");
cmd.arg("//depot/project/...");
assert_eq!(args_of(&cmd), vec!["filelog", "//depot/project/..."]);
}
#[test]
fn changelist() {
let filelog = FileLog::new("p4", GlobalOpts::default()).changelist("100");
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-c", "100"]);
}
#[test]
fn content_history() {
let filelog = FileLog::new("p4", GlobalOpts::default()).content_history();
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-h"]);
}
#[test]
fn follow_branches() {
let filelog = FileLog::new("p4", GlobalOpts::default()).follow_branches(true);
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-i"]);
}
#[test]
fn full_description() {
let filelog = FileLog::new("p4", GlobalOpts::default()).full_description();
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-l"]);
}
#[test]
fn truncated_description() {
let filelog = FileLog::new("p4", GlobalOpts::default()).truncated_description();
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-L"]);
}
#[test]
fn limit() {
let filelog = FileLog::new("p4", GlobalOpts::default()).limit(5);
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-m", "5"]);
}
#[test]
fn skip_promoted_tasks() {
let filelog = FileLog::new("p4", GlobalOpts::default())
.content_history()
.skip_promoted_tasks(true);
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-h", "-p"]);
}
#[test]
fn skip_promoted_tasks_accessors() {
let mut filelog = FileLog::new("p4", GlobalOpts::default())
.full_description()
.content_history();
filelog.set_skip_promoted_tasks(true);
assert!(filelog.get_skip_promoted_tasks());
assert_eq!(
args_of(&filelog.setup_command("p4")),
["filelog", "-h", "-p", "-l"]
);
}
#[test]
fn ignore_non_contributory() {
let filelog = FileLog::new("p4", GlobalOpts::default()).ignore_non_contributory(true);
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-s"]);
}
#[test]
fn include_time() {
let filelog = FileLog::new("p4", GlobalOpts::default()).include_time(true);
let cmd = filelog.setup_command("p4");
assert_eq!(args_of(&cmd), vec!["filelog", "-t"]);
}
#[test]
fn all_options_order() {
let filelog = FileLog::new("p4", GlobalOpts::default())
.changelist("100")
.content_history()
.follow_branches(true)
.full_description()
.limit(5)
.skip_promoted_tasks(true)
.ignore_non_contributory(true)
.include_time(true);
let cmd = filelog.setup_command("p4");
assert_eq!(
args_of(&cmd),
vec![
"filelog", "-c", "100", "-h", "-p", "-i", "-l", "-m", "5", "-s", "-t",
]
);
}
}