use std::io;
use std::process::{Command, ExitStatus, Stdio};
use super::constants::{self, commands, flags};
use super::executor::JjExecutor;
impl JjExecutor {
fn interactive_argv_base(&self) -> Vec<String> {
let mut v = Vec::new();
if let Some(repo_path) = self.repo_path() {
v.push(flags::REPO_PATH.to_string());
v.push(repo_path.display().to_string());
}
v
}
fn spawn_interactive(&self, argv: &[String]) -> io::Result<ExitStatus> {
Command::new(constants::JJ_COMMAND)
.args(argv)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
}
pub fn squash_into_argv(&self, source: &str, destination: &str) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.extend(
[commands::SQUASH, "--from", source, "--into", destination]
.iter()
.map(|s| s.to_string()),
);
v
}
pub fn squash_into_interactive(
&self,
source: &str,
destination: &str,
) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.squash_into_argv(source, destination))
}
pub fn describe_edit_argv(&self, revision: &str) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.extend(
[commands::DESCRIBE, "-r", revision, flags::EDITOR_FLAG]
.iter()
.map(|s| s.to_string()),
);
v
}
pub fn describe_edit_interactive(&self, revision: &str) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.describe_edit_argv(revision))
}
pub fn split_argv(&self, revision: &str) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.extend(
[commands::SPLIT, "-r", revision]
.iter()
.map(|s| s.to_string()),
);
v
}
pub fn split_interactive(&self, revision: &str) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.split_argv(revision))
}
pub fn diffedit_argv(&self, revision: &str) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.extend(
[commands::DIFFEDIT, flags::REVISION, revision]
.iter()
.map(|s| s.to_string()),
);
v
}
pub fn diffedit_interactive(&self, revision: &str) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.diffedit_argv(revision))
}
pub fn diffedit_file_argv(&self, revision: &str, file: &str) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.extend(
[commands::DIFFEDIT, flags::REVISION, revision, file]
.iter()
.map(|s| s.to_string()),
);
v
}
pub fn diffedit_file_interactive(&self, revision: &str, file: &str) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.diffedit_file_argv(revision, file))
}
pub fn resolve_argv(&self, file_path: &str, revision: Option<&str>) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.push(commands::RESOLVE.to_string());
if let Some(rev) = revision {
v.push(flags::REVISION.to_string());
v.push(rev.to_string());
}
v.push(file_path.to_string());
v
}
pub fn resolve_interactive(
&self,
file_path: &str,
revision: Option<&str>,
) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.resolve_argv(file_path, revision))
}
pub fn arrange_argv(&self, revset: Option<&str>) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.push(commands::ARRANGE.to_string());
if let Some(rev) = revset {
v.push(flags::REVISION.to_string());
v.push(rev.to_string());
}
v
}
pub fn arrange_interactive(&self, revset: Option<&str>) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.arrange_argv(revset))
}
pub fn bisect_run_argv(&self, good: &str, bad: &str, command: &str) -> Vec<String> {
let mut v = self.interactive_argv_base();
let range = format!("{}..{}", good, bad);
v.extend(
[
commands::BISECT,
commands::BISECT_RUN,
"--range",
range.as_str(),
"--",
"bash",
"-c",
command,
]
.iter()
.map(|s| s.to_string()),
);
v
}
pub fn bisect_run_interactive(
&self,
good: &str,
bad: &str,
command: &str,
) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.bisect_run_argv(good, bad, command))
}
pub fn run_argv(&self, revset: &str, command: &str) -> Vec<String> {
let mut v = self.interactive_argv_base();
v.extend(
[
commands::RUN,
flags::REVISION,
revset,
"--",
"bash",
"-c",
command,
]
.iter()
.map(|s| s.to_string()),
);
v
}
pub fn run_interactive(&self, revset: &str, command: &str) -> io::Result<ExitStatus> {
self.spawn_interactive(&self.run_argv(revset, command))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn interactive_argv_builders_produce_real_command_lines() {
let jj = JjExecutor::new();
assert_eq!(
jj.describe_edit_argv("abc123"),
["describe", "-r", "abc123", "--editor"]
);
assert_eq!(jj.split_argv("abc123"), ["split", "-r", "abc123"]);
assert_eq!(
jj.squash_into_argv("src1", "dst2"),
["squash", "--from", "src1", "--into", "dst2"]
);
assert_eq!(jj.diffedit_argv("abc"), ["diffedit", "-r", "abc"]);
assert_eq!(
jj.diffedit_file_argv("abc", "src/main.rs"),
["diffedit", "-r", "abc", "src/main.rs"]
);
assert_eq!(
jj.resolve_argv("file.txt", Some("abc")),
["resolve", "-r", "abc", "file.txt"]
);
assert_eq!(jj.resolve_argv("file.txt", None), ["resolve", "file.txt"]);
assert_eq!(
jj.arrange_argv(Some("main..@")),
["arrange", "-r", "main..@"]
);
assert_eq!(
jj.bisect_run_argv("good1", "bad2", "cargo test"),
[
"bisect",
"run",
"--range",
"good1..bad2",
"--",
"bash",
"-c",
"cargo test"
]
);
assert_eq!(
jj.run_argv("mutable()", "cargo test"),
["run", "-r", "mutable()", "--", "bash", "-c", "cargo test"]
);
}
#[test]
fn interactive_argv_includes_repo_path_when_set() {
let jj = JjExecutor::with_repo_path(PathBuf::from("/tmp/repo"));
assert_eq!(
jj.split_argv("abc"),
["-R", "/tmp/repo", "split", "-r", "abc"]
);
assert_eq!(
jj.run_argv("main", "true"),
[
"-R",
"/tmp/repo",
"run",
"-r",
"main",
"--",
"bash",
"-c",
"true"
]
);
}
}