use std::{borrow::Cow, convert::TryInto};
use git_object::bstr::{BStr, BString, ByteSlice};
use crate::{store::packed, PartialNameRef};
impl packed::Buffer {
pub fn try_find<'a, Name, E>(&self, name: Name) -> Result<Option<packed::Reference<'_>>, Error>
where
Name: TryInto<PartialNameRef<'a>, Error = E>,
Error: From<E>,
{
let name = name.try_into()?;
for inbetween in &["", "tags", "heads", "remotes"] {
let (name, was_absolute) = if name.0.starts_with_str(b"refs/") {
(Cow::Borrowed(name.0), true)
} else {
let mut full_name: BString = format!(
"refs/{}",
if inbetween.is_empty() {
"".to_string()
} else {
format!("{}/", inbetween)
}
)
.into();
full_name.extend_from_slice(name.0);
(Cow::Owned(full_name), false)
};
match self.binary_search_by(name.as_ref()) {
Ok(line_start) => {
return Ok(Some(
packed::decode::reference::<()>(&self.as_ref()[line_start..])
.map_err(|_| Error::Parse)?
.1,
))
}
Err((parse_failure, _)) => {
if parse_failure {
return Err(Error::Parse);
} else if was_absolute {
return Ok(None);
} else {
continue;
}
}
}
}
Ok(None)
}
pub fn find<'a, Name, E>(&self, name: Name) -> Result<packed::Reference<'_>, existing::Error>
where
Name: TryInto<PartialNameRef<'a>, Error = E>,
Error: From<E>,
{
match self.try_find(name) {
Ok(Some(r)) => Ok(r),
Ok(None) => Err(existing::Error::NotFound),
Err(err) => Err(existing::Error::Find(err)),
}
}
pub(in crate::store::packed) fn binary_search_by(&self, full_name: &BStr) -> Result<usize, (bool, usize)> {
let a = self.as_ref();
let search_start_of_record = |ofs: usize| {
a[..ofs]
.rfind(b"\n")
.and_then(|pos| {
let candidate = pos + 1;
a.get(candidate).and_then(|b| {
if *b == b'^' {
a[..pos].rfind(b"\n").map(|pos| pos + 1)
} else {
Some(candidate)
}
})
})
.unwrap_or(0)
};
let mut encountered_parse_failure = false;
a.binary_search_by_key(&full_name.as_ref(), |b: &u8| {
let ofs = b as *const u8 as usize - a.as_ptr() as usize;
let line = &a[search_start_of_record(ofs)..];
packed::decode::reference::<()>(line)
.map(|(_rest, r)| r.name.as_bstr().as_ref())
.map_err(|err| {
encountered_parse_failure = true;
err
})
.unwrap_or(&[])
})
.map(search_start_of_record)
.map_err(|pos| (encountered_parse_failure, search_start_of_record(pos)))
}
}
mod error {
use std::convert::Infallible;
use quick_error::quick_error;
quick_error! {
#[derive(Debug)]
#[allow(missing_docs)]
pub enum Error {
RefnameValidation(err: crate::name::Error) {
display("The ref name or path is not a valid ref name")
from()
source(err)
}
Parse {
display("The reference could not be parsed")
}
}
}
impl From<Infallible> for Error {
fn from(_: Infallible) -> Self {
unreachable!("this impl is needed to allow passing a known valid partial path as parameter")
}
}
}
pub use error::Error;
pub mod existing {
use quick_error::quick_error;
quick_error! {
#[derive(Debug)]
#[allow(missing_docs)]
pub enum Error {
Find(err: super::Error) {
display("The find operation failed")
from()
source(err)
}
NotFound {
display("The reference did not exist even though that was expected")
}
}
}
}