use std::sync::atomic::AtomicBool;
use super::{
FetchFinishError, FetchFinishFailure, FetchPlanError, FetchPrepareLimits, FetchReady,
FetchReport, FetchUpdateLimits,
};
use crate::refs::{
Backend, FilesTransactionLimits, FilesTransactionOptions, RefEdit, RefEditOutcome, References,
TransactionError,
};
use crate::{OpenLimits, OpenOptions, Repository};
#[derive(Clone, Copy, Debug, Default)]
#[non_exhaustive]
pub struct FetchFinishLimits {
pub open: OpenLimits,
pub preparation: FetchPrepareLimits,
pub files_transaction: FilesTransactionLimits,
}
impl FetchFinishLimits {
#[must_use]
pub fn with_files_transaction(mut self, limits: FilesTransactionLimits) -> Self {
self.files_transaction = limits;
self
}
#[must_use]
pub fn with_open(mut self, open: OpenLimits) -> Self {
self.open = open;
self
}
#[must_use]
pub fn with_preparation(mut self, preparation: FetchPrepareLimits) -> Self {
self.preparation = preparation;
self
}
}
#[derive(Clone, Debug, Default)]
pub struct FetchFinishOptions {
limits: FetchUpdateLimits,
admission: FetchFinishLimits,
}
impl FetchFinishOptions {
#[must_use]
pub fn with_limits(mut self, limits: FetchUpdateLimits) -> Self {
self.limits = limits;
self
}
#[must_use]
pub fn with_admission_limits(mut self, admission: FetchFinishLimits) -> Self {
self.admission = admission;
self
}
pub fn finish(
&self,
ready: FetchReady,
cancel: &AtomicBool,
) -> Result<FetchReport, FetchFinishError> {
ready.finish_controlled(self.limits, self.admission, cancel)
}
}
pub(super) fn admit(
repository: &Repository,
limits: FetchFinishLimits,
cancel: &AtomicBool,
) -> Result<Repository, Box<FetchFinishFailure>> {
super::check_cancelled(cancel).map_err(FetchFinishFailure::BeforePublication)?;
super::worktree::admit_controlled(repository, cancel).map_err(FetchFinishFailure::Safety)?;
let current = OpenOptions::default()
.with_limits(limits.open)
.open(repository.git_dir(), cancel)
.map_err(FetchFinishFailure::Reopen)?;
if current.object_dir() != repository.object_dir()
|| current.object_format() != repository.object_format()
{
return Err(
FetchFinishFailure::BeforePublication(super::FetchError::Unsupported(
"fetch repository layout changed",
))
.into(),
);
}
if current.reference_backend() != repository.reference_backend() {
return Err(
FetchFinishFailure::BeforePublication(super::FetchError::Unsupported(
"fetch reference backend changed",
))
.into(),
);
}
super::worktree::admit_controlled(¤t, cancel).map_err(FetchFinishFailure::Safety)?;
let refs = current
.references()
.map_err(FetchPlanError::Reference)
.map_err(FetchFinishFailure::Safety)?;
let refs = refs.with_reftable_limits(limits.preparation.reftable);
let listed = refs
.list_controlled(
limits.preparation.max_ref_entries,
limits.preparation.max_ref_bytes,
cancel,
)
.map_err(FetchPlanError::Reference)
.map_err(FetchFinishFailure::Safety)?;
super::worktree::check_head_controlled(¤t, &listed, limits.preparation, cancel)
.map_err(FetchFinishFailure::Safety)?;
if current.shallow_roots().iter().len() > limits.preparation.max_shallow_roots {
return Err(
FetchFinishFailure::Safety(FetchPlanError::PrepareLimit("shallow roots")).into(),
);
}
Ok(current)
}
pub(super) fn transaction(
refs: &References<'_>,
backend: Backend,
edits: &[RefEdit],
limits: FetchFinishLimits,
cancel: &AtomicBool,
) -> Result<Vec<RefEditOutcome>, TransactionError> {
match backend {
Backend::Files => refs.transaction_files_with_options(
edits,
FilesTransactionOptions::GIT_DEFAULT.with_limits(limits.files_transaction),
cancel,
),
Backend::Reftable => refs.transaction_controlled(edits, cancel),
}
}