use std::convert::TryInto; use git_object::bstr::{BStr, BString, ByteSlice}; use crate::{store_impl::packed, FullNameRef, PartialNameRef}; /// packed-refs specific functionalityimpl packed::Buffer { /// Find a reference with the given `name` and return it. /// /// Note that it will look it up verbatim and does not deal with namespaces or special prefixes like /// `main-worktree/` or `worktrees/<name>/`, as this is left to the caller. pub fn try_find<'a, Name, E>(&self, name: Name) -> Result<Option<packed::Reference<'_>>, Error> where Name: TryInto<&'a PartialNameRef, Error = E>, Error: From<E>, { let name = name.try_into()?; let mut buf = BString::default(); for inbetween in &["", "tags", "heads", "remotes"] { let (name, was_absolute) = if name.looks_like_full_name() { 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(true, inbetween, &mut buf); (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) -> Result<Option<packed::Reference<'_>>, Error> { match self.binary_search_by(name.as_bstr()) { 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 { Err(Error::Parse) } else { Ok(None) } } } } /// Find a reference with the given `name` and return it. pub fn find<'a, Name, E>(&self, name: Name) -> Result<packed::Reference<'_>, existing::Error> where Name: TryInto<&'a PartialNameRef, 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)), } } /// Perform a binary search where `Ok(pos)` is the beginning of the line that matches `name` perfectly and `Err(pos)` /// is the beginning of the line at which `name` could be inserted to still be in sort order. pub(in crate::store_impl::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! { /// The error returned by [`find()`][super::packed::Buffer::find()] #[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! { /// The error returned by [`find_existing()`][super::packed::Buffer::find_existing()] #[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") } } }} 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), }}
use std::convert::TryInto;
use git_object::bstr::{BStr, BString, ByteSlice};
use crate::{store_impl::packed, FullNameRef, PartialNameRef};
/// packed-refs specific functionality
impl packed::Buffer {
/// Find a reference with the given `name` and return it.
///
/// Note that it will look it up verbatim and does not deal with namespaces or special prefixes like
/// `main-worktree/` or `worktrees/<name>/`, as this is left to the caller.
pub fn try_find<'a, Name, E>(&self, name: Name) -> Result<Option<packed::Reference<'_>>, Error>
where
Name: TryInto<&'a PartialNameRef, Error = E>,
Error: From<E>,
{
let name = name.try_into()?;
let mut buf = BString::default();
for inbetween in &["", "tags", "heads", "remotes"] {
let (name, was_absolute) = if name.looks_like_full_name() {
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(true, inbetween, &mut buf);
(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) -> Result<Option<packed::Reference<'_>>, Error> {
match self.binary_search_by(name.as_bstr()) {
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 {
Err(Error::Parse)
} else {
Ok(None)
}
}
}
}
/// Find a reference with the given `name` and return it.
pub fn find<'a, Name, E>(&self, name: Name) -> Result<packed::Reference<'_>, existing::Error>
where
Name: TryInto<&'a PartialNameRef, 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)),
}
}
/// Perform a binary search where `Ok(pos)` is the beginning of the line that matches `name` perfectly and `Err(pos)`
/// is the beginning of the line at which `name` could be inserted to still be in sort order.
pub(in crate::store_impl::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! {
/// The error returned by [`find()`][super::packed::Buffer::find()]
#[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! {
/// The error returned by [`find_existing()`][super::packed::Buffer::find_existing()]
#[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")
}
}
}
}
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),
}
}