use std::convert::TryFrom;
use bstr::ByteSlice;
use git_hash::ObjectId;
use crate::{
file::loose::reference::logiter::must_be_io_err,
mutable,
store::{
file,
file::{log, loose},
packed,
},
FullName, Namespace,
};
#[derive(Debug)]
pub enum Reference<'p> {
Packed(packed::Reference<'p>),
Loose(loose::Reference),
}
impl<'p> TryFrom<Reference<'p>> for loose::Reference {
type Error = ();
fn try_from(value: Reference<'p>) -> Result<Self, Self::Error> {
match value {
Reference::Loose(l) => Ok(l),
Reference::Packed(_) => Err(()),
}
}
}
impl<'p> TryFrom<Reference<'p>> for packed::Reference<'p> {
type Error = ();
fn try_from(value: Reference<'p>) -> Result<Self, Self::Error> {
match value {
Reference::Loose(_) => Err(()),
Reference::Packed(p) => Ok(p),
}
}
}
impl<'p> Reference<'p> {
pub fn log_exists(&self, store: &file::Store) -> bool {
match self {
Reference::Loose(r) => r.log_exists(store),
Reference::Packed(p) => store.reflog_exists(p.name).expect("infallible name conversion"),
}
}
pub fn peel_to_id_in_place<E: std::error::Error + Send + Sync + 'static>(
&mut self,
store: &file::Store,
packed: Option<&packed::Buffer>,
find: impl FnMut(git_hash::ObjectId, &mut Vec<u8>) -> Result<Option<(git_object::Kind, &[u8])>, E>,
) -> Result<ObjectId, crate::store::file::loose::reference::peel::to_id::Error> {
match self {
Reference::Loose(r) => r.peel_to_id_in_place(store, packed, find).map(ToOwned::to_owned),
Reference::Packed(p) => {
if let Some(object) = p.object {
p.target = object;
}
p.object = None;
Ok(p.target())
}
}
}
pub fn peel_one_level<'p2>(
&self,
store: &file::Store,
packed: Option<&'p2 packed::Buffer>,
) -> Option<Result<Reference<'p2>, crate::store::file::loose::reference::peel::Error>> {
match self {
Reference::Loose(r) => r.follow_symbolic(store, packed),
Reference::Packed(p) => packed
.and_then(|packed| packed.find(p.name).ok().flatten()) .and_then(|np| {
p.object.and(np.object).map(|peeled| {
Ok(Reference::Packed(packed::Reference {
name: np.name,
target: peeled,
object: None,
}))
})
}),
}
}
pub fn log_iter_rev<'b>(
&self,
store: &file::Store,
buf: &'b mut [u8],
) -> std::io::Result<Option<log::iter::Reverse<'b, std::fs::File>>> {
match self {
Reference::Loose(r) => r.log_iter_rev(store, buf),
Reference::Packed(p) => store.reflog_iter_rev(p.name, buf).map_err(must_be_io_err),
}
}
pub fn log_iter<'a, 'b: 'a>(
&'a self,
store: &file::Store,
buf: &'b mut Vec<u8>,
) -> std::io::Result<Option<impl Iterator<Item = Result<log::Line<'b>, log::iter::decode::Error>> + 'a>> {
match self {
Reference::Loose(r) => store.reflog_iter(r.name.borrow(), buf).map_err(must_be_io_err),
Reference::Packed(p) => store.reflog_iter(p.name, buf).map_err(must_be_io_err),
}
}
pub fn kind(&self) -> crate::Kind {
match self {
Reference::Loose(r) => r.kind(),
Reference::Packed(_) => crate::Kind::Peeled,
}
}
pub fn into_target(self) -> mutable::Target {
match self {
Reference::Packed(p) => mutable::Target::Peeled(p.object()),
Reference::Loose(r) => r.target,
}
}
pub fn is_packed(&self) -> bool {
match self {
Reference::Packed(_) => true,
Reference::Loose(_) => false,
}
}
pub fn name(&self) -> FullName<'_> {
match self {
Reference::Packed(p) => p.name,
Reference::Loose(l) => l.name.borrow(),
}
}
pub fn name_without_namespace(&self, namespace: &Namespace) -> Option<FullName<'_>> {
self.name()
.0
.as_bstr()
.strip_prefix(namespace.0.as_bstr().as_ref())
.map(|stripped| FullName(stripped.as_bstr()))
}
pub fn target(&self) -> mutable::Target {
match self {
Reference::Packed(p) => mutable::Target::Peeled(p.target()),
Reference::Loose(l) => l.target.clone(),
}
}
}