#![allow(clippy::empty_docs)]
use std::ops::Deref;
use gix_hash::{oid, ObjectId};
use crate::{object::find, Id, Object};
impl<'repo> Id<'repo> {
pub fn object(&self) -> Result<Object<'repo>, find::existing::Error> {
self.repo.find_object(self.inner)
}
pub fn header(&self) -> Result<gix_odb::find::Header, find::existing::Error> {
self.repo.find_header(self.inner)
}
pub fn try_object(&self) -> Result<Option<Object<'repo>>, find::Error> {
self.repo.try_find_object(self.inner)
}
pub fn try_header(&self) -> Result<Option<gix_odb::find::Header>, find::Error> {
self.repo.try_find_header(self.inner)
}
pub fn shorten(&self) -> Result<gix_hash::Prefix, shorten::Error> {
let hex_len = self.repo.config.hex_len.map_or_else(
|| self.repo.objects.packed_object_count().map(calculate_auto_hex_len),
Ok,
)?;
let prefix = gix_odb::store::prefix::disambiguate::Candidate::new(self.inner, hex_len)
.expect("BUG: internal hex-len must always be valid");
self.repo
.objects
.disambiguate_prefix(prefix)?
.ok_or(shorten::Error::NotFound { oid: self.inner })
}
pub fn shorten_or_id(&self) -> gix_hash::Prefix {
self.shorten().unwrap_or_else(|_| self.inner.into())
}
}
fn calculate_auto_hex_len(num_packed_objects: u64) -> usize {
let mut len = 64 - num_packed_objects.leading_zeros();
len = (len + 1) / 2;
len.max(7) as usize
}
pub mod shorten {
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
PackedObjectsCount(#[from] gix_odb::store::load_index::Error),
#[error(transparent)]
DisambiguatePrefix(#[from] gix_odb::store::prefix::disambiguate::Error),
#[error("Id could not be shortened as the object with id {} could not be found", .oid)]
NotFound { oid: gix_hash::ObjectId },
}
}
impl Deref for Id<'_> {
type Target = oid;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'repo> Id<'repo> {
pub(crate) fn from_id(id: impl Into<ObjectId>, repo: &'repo crate::Repository) -> Self {
Id { inner: id.into(), repo }
}
pub fn detach(self) -> ObjectId {
self.inner
}
}
impl<'repo> Id<'repo> {
pub fn ancestors(&self) -> crate::revision::walk::Platform<'repo> {
crate::revision::walk::Platform::new(Some(self.inner), self.repo)
}
}
mod impls {
use std::{cmp::Ordering, hash::Hasher};
use gix_hash::{oid, ObjectId};
use crate::{Id, Object, ObjectDetached};
impl std::hash::Hash for Id<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<'a> PartialOrd<Id<'a>> for Id<'a> {
fn partial_cmp(&self, other: &Id<'a>) -> Option<Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl<'repo> PartialEq<Id<'repo>> for Id<'repo> {
fn eq(&self, other: &Id<'repo>) -> bool {
self.inner == other.inner
}
}
impl PartialEq<ObjectId> for Id<'_> {
fn eq(&self, other: &ObjectId) -> bool {
&self.inner == other
}
}
impl<'repo> PartialEq<Id<'repo>> for ObjectId {
fn eq(&self, other: &Id<'repo>) -> bool {
self == &other.inner
}
}
impl PartialEq<oid> for Id<'_> {
fn eq(&self, other: &oid) -> bool {
self.inner == other
}
}
impl<'repo> PartialEq<Object<'repo>> for Id<'repo> {
fn eq(&self, other: &Object<'repo>) -> bool {
self.inner == other.id
}
}
impl PartialEq<ObjectDetached> for Id<'_> {
fn eq(&self, other: &ObjectDetached) -> bool {
self.inner == other.id
}
}
impl std::fmt::Debug for Id<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::fmt::Display for Id<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl AsRef<oid> for Id<'_> {
fn as_ref(&self) -> &oid {
&self.inner
}
}
impl<'repo> From<Id<'repo>> for ObjectId {
fn from(v: Id<'repo>) -> Self {
v.inner
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size_of_oid() {
let actual = std::mem::size_of::<Id<'_>>();
let ceiling = 32;
assert!(
actual <= ceiling,
"size of oid shouldn't change without notice: {actual} <= {ceiling}"
);
}
}