use crate::term::info;
use anyhow::{bail, Result};
use git2::{
build::{CheckoutBuilder, RepoBuilder},
FetchOptions, Progress, RemoteCallbacks,
};
use std::{
cell::RefCell,
env,
io::{self, Write},
path::{Path, PathBuf},
process::Command,
};
struct State {
progress: Option<Progress<'static>>,
total: usize,
current: usize,
path: Option<PathBuf>,
newline: bool,
}
const GIT_URL: &str = "https://github.com/real-logic/simple-binary-encoding.git";
const CHECKOUT_DIR: &str = ".simple-binary-encoding";
const GRADLEW_CMD: &str = "./gradlew";
const SBE_VERSION_FILE: &str = "version.txt";
pub fn clean() -> Result<()> {
rm_repo_folder()?;
let version_file = Path::new(super::SBE_VERSION_FILE);
if version_file.exists() {
info("Removing SBE jar and version file")?;
let version = std::fs::read_to_string(version_file)?;
let jar = super::SBE_JAR_FORMAT.replace("{version}", version.trim());
std::fs::remove_file(version_file)?;
std::fs::remove_file(Path::new(jar.as_str()))?;
}
Ok(())
}
fn rm_repo_folder() -> Result<()> {
let dir = Path::new(CHECKOUT_DIR);
if dir.exists() {
std::fs::remove_dir_all(dir)?;
} else {
bail!("Directory does not exist");
}
Ok(())
}
pub fn build() -> Result<()> {
let output = Command::new(GRADLEW_CMD)
.current_dir(Path::new(CHECKOUT_DIR))
.spawn()
.expect("Unable to spawn sbe build")
.wait_with_output()
.expect("Unable to execute sbe build");
if !output.status.success() {
let stderr = std::str::from_utf8(&output.stderr).unwrap();
bail!("SBE build failed\n{}", stderr);
}
copy_sbe_jar()?;
Ok(())
}
pub fn copy_sbe_jar() -> Result<()> {
let version_file = Path::new(CHECKOUT_DIR).join(SBE_VERSION_FILE);
let version = std::fs::read_to_string(version_file.clone())?;
let jar = super::SBE_JAR_FORMAT.replace("{version}", version.trim());
let src = Path::new(CHECKOUT_DIR).join("sbe-all").join("build").join("libs").join(&jar);
let dst = Path::new(&jar);
std::fs::copy(src, dst)?;
std::fs::copy(version_file, super::SBE_VERSION_FILE)?;
Ok(())
}
pub fn clone() -> Result<()> {
let state =
RefCell::new(State { progress: None, total: 0, current: 0, path: None, newline: false });
let mut cb = RemoteCallbacks::new();
cb.credentials(git_credentials_callback);
cb.transfer_progress(|stats| {
let mut state = state.borrow_mut();
state.progress = Some(stats.to_owned());
print(&mut state);
true
});
let mut co = CheckoutBuilder::new();
co.progress(|path, cur, total| {
let mut state = state.borrow_mut();
state.path = path.map(|p| p.to_path_buf());
state.current = cur;
state.total = total;
print(&mut state);
});
let mut fo = FetchOptions::new();
fo.remote_callbacks(cb);
RepoBuilder::new()
.fetch_options(fo)
.with_checkout(co)
.clone(GIT_URL, Path::new(CHECKOUT_DIR))?;
println!();
Ok(())
}
fn git_credentials_callback(
_user: &str,
_user_from_url: Option<&str>,
_cred: git2::CredentialType,
) -> Result<git2::Cred, git2::Error> {
let user = _user_from_url.unwrap_or("git");
git2::Cred::ssh_key(
user,
None,
std::path::Path::new(&format!("{}/.ssh/id_rsa", env::var("HOME").unwrap())),
None,
)
}
fn print(state: &mut State) {
let stats = state.progress.as_ref().unwrap();
let network_pct = (100 * stats.received_objects()) / stats.total_objects();
let index_pct = (100 * stats.indexed_objects()) / stats.total_objects();
let co_pct = if state.total > 0 { (100 * state.current) / state.total } else { 0 };
let kbytes = stats.received_bytes() / 1024;
if stats.received_objects() == stats.total_objects() {
if !state.newline {
println!();
state.newline = true;
}
print!("Resolving deltas {}/{}\r", stats.indexed_deltas(), stats.total_deltas());
} else {
print!(
"net {:3}% ({:4} kb, {:5}/{:5}) / idx {:3}% ({:5}/{:5}) \
/ chk {:3}% ({:4}/{:4}) {}\r",
network_pct,
kbytes,
stats.received_objects(),
stats.total_objects(),
index_pct,
stats.indexed_objects(),
stats.total_objects(),
co_pct,
state.current,
state.total,
state
.path
.as_ref()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_default()
)
}
io::stdout().flush().unwrap();
}