pray-core 1.19.0

Core library for Prayfile, the package manager for the language placed before inference
Documentation
use crate::paths::remove_path_if_exists;
use crate::resolve_git::{
    checkout_git_revision, ensure_git_remote_origin, finalize_git_repository, git_head_revision,
    mirror_git_cache_to_global, refresh_git_worktree, refresh_global_from_project,
    seed_git_cache_from_global,
};
use crate::resolve_git_clone::{apply_sparse_checkout, clone_git_cache};
use crate::resolve_git_command::{run_git_command, run_git_success};
use crate::resolve_git_lookup::is_git_checkout;
use crate::resolve_git_paths::{
    git_source_cache_directory, git_source_cache_directory_with_subdir,
};
use crate::{PrayError, PrayResult};
use std::fs;
use std::path::{Path, PathBuf};

pub(crate) fn ensure_git_repository(
    project_root: &Path,
    clone_url: &str,
    refresh: bool,
    pinned_revision: Option<&str>,
    sparse_subdir: Option<&str>,
) -> PrayResult<(PathBuf, String)> {
    let shared = git_source_cache_directory(project_root, clone_url);
    ensure_shared_git_repository(project_root, clone_url, &shared, refresh, pinned_revision)?;
    let checkout = git_source_cache_directory_with_subdir(project_root, clone_url, sparse_subdir);
    if checkout != shared {
        ensure_linked_worktree(&shared, &checkout)?;
        apply_sparse_checkout(&checkout, sparse_subdir)?;
        if let Some(revision) = pinned_revision {
            checkout_git_revision(&checkout, clone_url, revision, refresh)?;
        } else if refresh {
            let shared_head = git_head_revision(&shared)?;
            run_git_success(&checkout, &["reset", "--hard", &shared_head])?;
        }
        let revision = git_head_revision(&checkout)?;
        return finalize_git_repository(clone_url, &checkout, revision);
    }
    apply_sparse_checkout(&shared, None)?;
    let revision = git_head_revision(&shared)?;
    finalize_git_repository(clone_url, &shared, revision)
}

fn ensure_shared_git_repository(
    project_root: &Path,
    clone_url: &str,
    shared: &Path,
    refresh: bool,
    pinned_revision: Option<&str>,
) -> PrayResult<()> {
    if shared.join(".git").is_dir() {
        if let Some(revision) = pinned_revision {
            checkout_git_revision(shared, clone_url, revision, refresh)?;
        } else if refresh {
            refresh_git_worktree(shared, clone_url)?;
        }
        if refresh {
            let _ = refresh_global_from_project(clone_url, shared);
        }
        return Ok(());
    }
    if shared.exists() {
        remove_path_if_exists(shared)?;
    }
    if let Some(parent) = shared.parent() {
        fs::create_dir_all(parent)?;
    }
    let destination = shared
        .to_str()
        .ok_or_else(|| PrayError::Resolution(format!("invalid git cache path: {:?}", shared)))?;
    let seeded = seed_git_cache_from_global(clone_url, destination, project_root)?;
    if seeded {
        ensure_git_remote_origin(shared, clone_url)?;
    } else {
        clone_git_cache(project_root, clone_url, destination, false)?;
        let _ = mirror_git_cache_to_global(clone_url, shared);
    }
    apply_sparse_checkout(shared, None)?;
    if let Some(revision) = pinned_revision {
        checkout_git_revision(shared, clone_url, revision, true)?;
    } else if refresh && seeded {
        refresh_git_worktree(shared, clone_url)?;
    }
    if refresh && seeded {
        let _ = refresh_global_from_project(clone_url, shared);
    }
    Ok(())
}

fn ensure_linked_worktree(shared: &Path, checkout: &Path) -> PrayResult<()> {
    if same_object_store(shared, checkout) {
        return Ok(());
    }
    if checkout.exists() {
        remove_path_if_exists(checkout)?;
    }
    if let Some(parent) = checkout.parent() {
        fs::create_dir_all(parent)?;
    }
    let destination = checkout.to_str().ok_or_else(|| {
        PrayError::Resolution(format!("invalid git worktree path: {:?}", checkout))
    })?;
    if run_git_success(
        shared,
        &["worktree", "add", "--detach", "--no-checkout", destination],
    )
    .is_err()
    {
        run_git_success(shared, &["worktree", "add", "--detach", destination])?;
    }
    Ok(())
}

fn same_object_store(shared: &Path, checkout: &Path) -> bool {
    if !is_git_checkout(checkout) {
        return false;
    }
    match (git_common_dir(shared), git_common_dir(checkout)) {
        (Ok(left), Ok(right)) => left == right,
        _ => false,
    }
}

fn git_common_dir(repository: &Path) -> PrayResult<PathBuf> {
    let output = run_git_command(repository, &["rev-parse", "--git-common-dir"])?;
    if !output.status.success() {
        return Err(PrayError::Resolution(
            "git rev-parse --git-common-dir failed".to_string(),
        ));
    }
    let reported = String::from_utf8_lossy(&output.stdout).trim().to_string();
    let path = Path::new(&reported);
    let resolved = if path.is_absolute() {
        path.to_path_buf()
    } else {
        repository.join(path)
    };
    resolved.canonicalize().map_err(|error| {
        PrayError::Resolution(format!("invalid git-common-dir {reported}: {error}"))
    })
}