use super::AtomicEvent;
use crate::auth::auth_utils::transform_url_for_preference;
use crate::auth::git_auth::setup_auth_callbacks;
use crate::bgit_error::BGitError;
use crate::config::global::BGitGlobalConfig;
use crate::rules::Rule;
use log::info;
use std::env;
use std::path::Path;
pub struct GitClone<'a> {
pub pre_check_rules: Vec<Box<dyn Rule + Send + Sync>>,
pub url: String,
pub global_config: &'a BGitGlobalConfig,
}
impl<'a> AtomicEvent<'a> for GitClone<'a> {
fn new(global_config: &'a BGitGlobalConfig) -> Self
where
Self: Sized,
{
GitClone {
pre_check_rules: vec![],
url: String::new(),
global_config,
}
}
fn get_name(&self) -> &str {
"git_clone"
}
fn get_action_description(&self) -> &str {
"Clone a Git repository"
}
fn add_pre_check_rule(&mut self, rule: Box<dyn Rule + Send + Sync>) {
self.pre_check_rules.push(rule);
}
fn get_pre_check_rule(&self) -> &Vec<Box<dyn Rule + Send + Sync>> {
&self.pre_check_rules
}
fn raw_execute(&self) -> Result<bool, Box<BGitError>> {
if self.url.is_empty() {
return Err(self.to_bgit_error("Repository URL is not set"));
}
let url = if let Some(new_url) =
transform_url_for_preference(&self.url, self.global_config.auth.preferred)
{
let preferred = self.global_config.auth.preferred;
info!(
"Using preferred auth ({:?}) URL: {} -> {}",
preferred, &self.url, &new_url
);
new_url
} else {
self.url.clone()
};
let repo_name = match url.split("/").last() {
Some(repo_name) => repo_name.strip_suffix(".git").unwrap_or(repo_name),
None => {
return Err(self.to_bgit_error("Failed to get repository name from URL"));
}
};
let fetch_options = self.create_fetch_options();
let mut builder = git2::build::RepoBuilder::new();
builder.fetch_options(fetch_options);
builder.clone(&url, Path::new(repo_name)).map_err(|e| {
self.to_bgit_error(&format!("Failed to clone repository: {e}. Please check your SSH keys or authentication setup."))
})?;
self.update_cwd_path()?;
Ok(true)
}
}
impl<'a> GitClone<'a> {
pub fn set_url(&mut self, url: &str) -> &mut Self {
self.url = url.to_owned();
self
}
fn update_cwd_path(&self) -> Result<(), Box<BGitError>> {
let repo_name = match self.url.split("/").last() {
Some(repo_name) => repo_name.strip_suffix(".git").unwrap_or(repo_name),
None => {
return Err(self.to_bgit_error("Failed to get repository name from URL"));
}
};
match env::set_current_dir(repo_name) {
Ok(_) => Ok(()),
Err(_) => Err(self.to_bgit_error("Failed to update current working directory path")),
}
}
fn create_fetch_options(&'a self) -> git2::FetchOptions<'a> {
let mut fetch_options = git2::FetchOptions::new();
fetch_options.remote_callbacks(setup_auth_callbacks(self.global_config));
fetch_options
}
}