use gix_error::{ErrorExt, ExnResult, Message, ResultExt, message};
use gix_object::bstr::{BStr, BString};
use crate::{FullNameRef, PartialNameRef, store_impl::packed};
impl packed::Buffer {
pub fn try_find<'a, Name, E>(&self, name: Name) -> ExnResult<Option<packed::Reference<'_>>>
where
Name: TryInto<&'a PartialNameRef, Error = E>,
Result<&'a PartialNameRef, E>: ResultExt<Success = &'a PartialNameRef>,
{
let name = name
.try_into()
.or_raise_erased(|| message("The ref name or path is not a valid ref name"))?;
let mut buf = BString::default();
for inbetween in &["", "tags", "heads", "remotes"] {
let (name, was_absolute) = if name.looks_like_full_name(false) {
let name = FullNameRef::new_unchecked(name.as_bstr());
let name = match transform_full_name_for_lookup(name) {
None => return Ok(None),
Some(name) => name,
};
(name, true)
} else {
let full_name = name.construct_full_name_ref(inbetween, &mut buf, false);
(full_name, false)
};
match self.try_find_full_name(name)? {
Some(r) => return Ok(Some(r)),
None if was_absolute => return Ok(None),
None => continue,
}
}
Ok(None)
}
pub(crate) fn try_find_full_name(&self, name: &FullNameRef) -> ExnResult<Option<packed::Reference<'_>>> {
match self.binary_search_by(name.as_bstr()) {
Ok(line_start) => {
let mut input = &self.as_ref()[line_start..];
packed::decode::reference(&mut input, self.object_hash).map(Some)
}
Err((parse_failure, _)) => {
if parse_failure {
Err(gix_error::corruption("Malformed packed reference record").raise())
} else {
Ok(None)
}
}
}
.or_raise_erased(|| Message::new("Could not decode packed reference").with("name", name.as_bstr()))
}
pub fn find<'a, Name, E>(&self, name: Name) -> ExnResult<packed::Reference<'_>>
where
Name: TryInto<&'a PartialNameRef, Error = E>,
Result<&'a PartialNameRef, E>: ResultExt<Success = &'a PartialNameRef>,
{
let name = name
.try_into()
.or_raise_erased(|| message("The ref name or path is not a valid ref name"))?;
self.try_find::<_, std::convert::Infallible>(name)?.ok_or_else(|| {
crate::file::find::NotFound {
name: name.to_partial_path().to_owned(),
}
.raise_erased()
})
}
pub(in crate::store_impl::packed) fn binary_search_by(&self, full_name: &BStr) -> Result<usize, (bool, usize)> {
let a = self.as_ref();
let mut encountered_parse_failure = false;
a.binary_search_by_key(&full_name.as_ref(), |b: &u8| {
let ofs = std::ptr::from_ref::<u8>(b) as usize - a.as_ptr() as usize;
let line = packed::decode::record_at_offset(a, ofs);
match packed::decode::name_at_record_start(line, self.object_hash) {
Some(name) => name,
None => {
encountered_parse_failure = true;
&[]
}
}
})
.map(|pos| packed::decode::record_start_at_offset(a, pos))
.map_err(|pos| {
(
encountered_parse_failure,
packed::decode::record_start_at_offset(a, pos),
)
})
}
}
pub(crate) fn transform_full_name_for_lookup(name: &FullNameRef) -> Option<&FullNameRef> {
match name.category_and_short_name() {
Some((c, sn)) => {
use crate::Category::*;
Some(match c {
MainRef | LinkedRef { .. } => FullNameRef::new_unchecked(sn),
Tag | RemoteBranch | LocalBranch | Bisect | Rewritten | Note => name,
MainPseudoRef | PseudoRef | LinkedPseudoRef { .. } | WorktreePrivate => return None,
})
}
None => Some(name),
}
}