use std::io;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
use tokio::process::Command;
use crate::error::Error;
use crate::metadata::{Metadata, get_crate_metadata_from_workspace};
use crate::utils::{self, DirectoryContents, get_contents};
pub(crate) async fn git_clone(url: &str, sha1: &str) -> Result<TempDir, Error> {
let out = TempDir::new()?;
let path = out.path().to_string_lossy();
let cmd = ["clone", url, path.as_ref(), "--revision", sha1, "--depth", "1"];
let result = Command::new("git").args(cmd.iter()).output().await?;
if !result.status.success() {
let stdout = String::from_utf8_lossy(&result.stdout).to_string();
let stderr = String::from_utf8_lossy(&result.stderr).to_string();
if stderr.contains("dumb http transport") {
return Err(Error::InvalidRepoUrl {
repo: String::from(url),
});
}
if stderr.contains(&format!("not our ref {sha1}")) {
return Err(Error::InvalidGitRef {
repo: String::from(url),
rev: String::from(sha1),
});
}
return Err(Error::Subprocess {
cmd: cmd.join(" "),
stdout,
stderr,
});
}
Ok(out)
}
#[derive(Debug)]
#[non_exhaustive]
pub struct Repository<'a> {
pub metadata: Metadata,
pub root: PathBuf,
pub path_in_vcs: &'a str,
_temp: Option<TempDir>,
}
impl<'a> Repository<'a> {
#[allow(unused)]
pub(crate) fn local<P: AsRef<Path>>(root: P, path_in_vcs: &'a str, name: &str) -> Result<Self, Error> {
let root = root.as_ref().to_owned();
let metadata = get_crate_metadata_from_workspace(root.clone(), path_in_vcs, name)?;
Ok(Repository {
metadata,
root,
path_in_vcs,
_temp: None,
})
}
pub(crate) async fn clone(url: &str, id: &str, path_in_vcs: &'a str, name: &str) -> Result<Self, Error> {
let temp = git_clone(url, id).await?;
let metadata = get_crate_metadata_from_workspace(temp.as_ref(), path_in_vcs, name)?;
Ok(Repository {
metadata,
root: temp.path().to_owned(),
path_in_vcs,
_temp: Some(temp),
})
}
pub(crate) fn cargo_toml(&self) -> PathBuf {
self.root.join(self.path_in_vcs).join("Cargo.toml")
}
pub(crate) fn file_contents(&self) -> Result<DirectoryContents, Error> {
let mut contents = get_contents(&self.root.join(self.path_in_vcs), &self.root)?;
contents
.files
.retain(|f| f.file_name().is_some_and(|s| s != "Cargo.toml"));
Ok(contents)
}
pub(crate) fn read_entry_to_bytes<P: AsRef<Path>>(&self, path: P) -> io::Result<Vec<u8>> {
utils::read_to_bytes(self.root.join(self.path_in_vcs).join(path))
}
#[allow(unused)]
pub(crate) fn read_entry_to_string<P: AsRef<Path>>(&self, path: P) -> io::Result<String> {
std::fs::read_to_string(self.root.join(self.path_in_vcs).join(path))
}
}