You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.1 KiB
156 lines
4.1 KiB
use crate::async_utils::try_join;
|
|
use crate::command_runner::CommandRunner;
|
|
use crate::symbols::Symbol;
|
|
use async_trait::async_trait;
|
|
use std::borrow::Borrow;
|
|
use std::error::Error;
|
|
use std::ffi::OsStr;
|
|
use std::marker::PhantomData;
|
|
use std::path::Path;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Checkout<_C, C, P, S, B> {
|
|
target: P,
|
|
source: S,
|
|
branch: B,
|
|
command_runner: C,
|
|
phantom: PhantomData<_C>,
|
|
}
|
|
|
|
impl<C, _C, P, S, B> Checkout<_C, C, P, S, B> {
|
|
pub fn new(target: P, source: S, branch: B, command_runner: C) -> Self {
|
|
Self {
|
|
target,
|
|
source,
|
|
branch,
|
|
command_runner,
|
|
phantom: PhantomData::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<C: CommandRunner, _C: Borrow<C>, P: AsRef<Path>, S, B> Checkout<C, _C, P, S, B> {
|
|
async fn run_git(&self, args: &[impl AsRef<OsStr>]) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
let mut new_args = Vec::with_capacity(args.len() + 2);
|
|
new_args.extend_from_slice(args!["-C", self.target.as_ref()]);
|
|
new_args.extend(args.iter().map(AsRef::as_ref));
|
|
self
|
|
.command_runner
|
|
.borrow()
|
|
.get_output("git", &new_args)
|
|
.await
|
|
}
|
|
}
|
|
|
|
#[async_trait(?Send)]
|
|
impl<C: CommandRunner, _C: Borrow<C>, P: AsRef<Path>, S: AsRef<str>, B: AsRef<str>> Symbol
|
|
for Checkout<C, _C, P, S, B>
|
|
{
|
|
async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
|
if !self.target.as_ref().exists() {
|
|
return Ok(false);
|
|
}
|
|
let fetch_head_f = async {
|
|
self
|
|
.run_git(args!["fetch", self.source, self.branch])
|
|
.await?;
|
|
// git rev-list resolves tag objects
|
|
self.run_git(&["rev-list", "-1", "FETCH_HEAD"]).await
|
|
};
|
|
let head_f = self.run_git(&["rev-list", "-1", "HEAD"]);
|
|
let (fetch_head, head) = try_join!(fetch_head_f, head_f)?;
|
|
Ok(fetch_head == head)
|
|
}
|
|
|
|
async fn execute(&self) -> Result<(), Box<dyn Error>> {
|
|
if self.target.as_ref().exists() {
|
|
self
|
|
.run_git(&["fetch", self.source.as_ref(), self.branch.as_ref()])
|
|
.await?;
|
|
self.run_git(&["merge", "FETCH_HEAD"]).await?;
|
|
} else {
|
|
self
|
|
.command_runner
|
|
.borrow()
|
|
.run_successfully(
|
|
"git",
|
|
args![
|
|
"clone",
|
|
"--depth",
|
|
"1",
|
|
"-b",
|
|
self.branch.as_ref(),
|
|
self.source.as_ref(),
|
|
self.target.as_ref(),
|
|
],
|
|
)
|
|
.await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::Checkout;
|
|
use crate::async_utils::run;
|
|
use crate::async_utils::sleep;
|
|
use crate::command_runner::CommandRunner;
|
|
use crate::symbols::Symbol;
|
|
use async_trait::async_trait;
|
|
use std::cell::RefCell;
|
|
use std::ffi::{OsStr, OsString};
|
|
use std::io::Result as IoResult;
|
|
use std::os::unix::process::ExitStatusExt;
|
|
use std::process::{ExitStatus, Output};
|
|
use std::time::{Duration, Instant};
|
|
|
|
struct DummyCommandRunner {
|
|
pub args: RefCell<Vec<Vec<OsString>>>,
|
|
}
|
|
#[async_trait(?Send)]
|
|
impl CommandRunner for DummyCommandRunner {
|
|
async fn run(&self, program: &str, args: &[&OsStr], stdin: &str) -> IoResult<Output> {
|
|
assert_eq!(program, "git");
|
|
assert_eq!(stdin, "");
|
|
sleep(Duration::from_millis(50)).await;
|
|
self
|
|
.args
|
|
.borrow_mut()
|
|
.push(args.iter().map(|a| a.to_os_string()).collect());
|
|
Ok(Output {
|
|
status: ExitStatus::from_raw(0),
|
|
stdout: vec![],
|
|
stderr: vec![],
|
|
})
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test() {
|
|
let c = DummyCommandRunner {
|
|
args: RefCell::new(vec![]),
|
|
};
|
|
let checkout: Checkout<DummyCommandRunner, _, _, _, _> =
|
|
Checkout::new("target", "source", "branch", &c);
|
|
let start = Instant::now();
|
|
assert!(run(checkout.target_reached()).unwrap());
|
|
let end = Instant::now();
|
|
let mut args = c.args.into_inner();
|
|
let first_two_args = &mut args[0..2];
|
|
first_two_args.sort_unstable();
|
|
assert_eq!(
|
|
first_two_args,
|
|
[
|
|
["-C", "target", "fetch", "source", "branch"],
|
|
["-C", "target", "rev-list", "-1", "HEAD"],
|
|
]
|
|
);
|
|
drop(first_two_args);
|
|
assert_eq!(args[2],
|
|
["-C", "target", "rev-list", "-1", "FETCH_HEAD"]);
|
|
|
|
assert!((end - start).as_millis() >= 100);
|
|
assert!((end - start).as_millis() < 150);
|
|
}
|
|
}
|