git-bug 0.2.4

A rust library for interfacing with git-bug repositories
Documentation
// git-bug-rs - A rust library for interfacing with git-bug repositories
//
// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This file is part of git-bug-rs/git-gub.
//
// You should have received a copy of the License along with this program.
// If not, see <https://www.gnu.org/licenses/agpl.txt>.

//! [`Ids`][`Id`] connect with their respective [`Entity`].

use std::{fmt::Display, marker::PhantomData};

use serde::{Deserialize, Serialize};

use super::Id;
use crate::replica::entity::Entity;

/// An [`Id`] that is scoped to an [`Entity`].
///
/// This is useful, as it allows limiting the semantic meaning of an [`Id`].
/// As such it can be used to maintain provenance information of [`Ids`][`Id`].
///
/// # Note
/// The only safe way to obtain a [`EntityId`] is via the
/// [`Replica::get_*`][`crate::replica::Replica::get`] functions.
// As explained in the toplevel doc comment, this Derive is only a implementation detail.
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Debug, Deserialize, Serialize)]
pub struct EntityId<E: Entity>(Id, PhantomData<E>);

impl<E: Entity> PartialEq for EntityId<E> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl<E: Entity> Ord for EntityId<E> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<E: Entity> PartialOrd for EntityId<E> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<E: Entity> Eq for EntityId<E> {}

impl<E: Entity> Copy for EntityId<E> {}

impl<E: Entity> Clone for EntityId<E> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<E: Entity> EntityId<E> {
    /// Convert an raw [`Id`] to an [`EntityId`] for an specific [`Entity`].
    ///
    /// # Safety
    /// The [`Id`] should be valid for the specified [`Entity`].
    #[must_use]
    pub unsafe fn from_id(value: Id) -> Self {
        Self(value, PhantomData)
    }

    /// Return the underlying [`Id`].
    #[must_use]
    pub fn as_id(&self) -> Id {
        self.0
    }

    /// Get the git reference path of this [`EntityId`].
    #[must_use]
    pub fn to_ref_path(&self) -> String {
        let mut id_buf = [0; 64];
        let id_str = self.as_id().hex_to_buf(&mut id_buf);

        format!("refs/{}/{id_str}", E::NAMESPACE)
    }
}

impl<E: Entity> Display for EntityId<E> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.as_id().fmt(f)
    }
}