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>.

//! An iterator over [`Entities`][`Entity`].

use gix::Repository;

use super::{
    Replica,
    entity::{Entity, EntityRead, id::entity_id::EntityId, read},
    get,
};
use crate::replica::entity::id::Id;

/// An iterator over the [`Ids`][`EntityId`] of all the [`Entities`][`Entity`] in a
/// [`Replica`][`super::Replica`].
#[derive(Debug)]
pub(super) struct EntityIdIter<E: Entity> {
    ids: Vec<Result<EntityId<E>, get::Error>>,
}

impl<E: Entity> EntityIdIter<E> {
    /// Construct a new Iterator over the [`Ids`][`EntityId`] of the
    /// [`Entities`][`Entity`] in a [`Repository`].
    ///
    /// # Note
    /// You probably want to use the higher level
    /// [`Replica::get_all_ids()`][`super::Replica::get_all_ids`] method
    /// instead.
    pub(super) fn new(repo: &Repository) -> Result<Self, get::Error> {
        let ids = {
            // TODO(@bpeetz): Ideally we would not have to allocate this vector here. <2025-05-24>
            repo.references()
                .map_err(get::Error::PackedBufferOpen)?
                .prefixed(format!("refs/{}/", E::NAMESPACE).as_bytes())
                .map_err(|err| get::Error::RefsIterPrefixed {
                    nasp: E::NAMESPACE,
                    error: err,
                })?
                .map(|maybe_ref| {
                    let reference = maybe_ref.map_err(|err| get::Error::RefGet(err.to_string()))?;
                    let name = reference.name();

                    let id = name.file_name();
                    let id = {
                        Id::from_hex(id).map_err(|err| get::Error::ParseAsHex {
                            id: id.to_string(),
                            error: err,
                        })?
                    };

                    // Safety:
                    // We read the Id from this Entity's namespace.
                    // As such, it is valid.
                    Ok::<EntityId<E>, get::Error>(unsafe { EntityId::from_id(id) })
                })
                .collect::<Vec<_>>()
        };

        Ok(Self { ids })
    }
}

impl<E: Entity> Iterator for EntityIdIter<E> {
    type Item = Result<EntityId<E>, get::Error>;

    fn next(&mut self) -> Option<Self::Item> {
        self.ids.pop()
    }
}

/// An iterator over the [`Entities`][`Entity`] in a
/// [`Replica`][`super::Replica`].
#[derive(Debug)]
pub(super) struct EntityIter<'a, E: Entity> {
    replica: &'a Replica,
    inner: EntityIdIter<E>,
}

impl<E: Entity + EntityRead> Iterator for EntityIter<'_, E> {
    type Item = Result<Result<E, read::Error<E>>, get::Error>;

    fn next(&mut self) -> Option<Self::Item> {
        self.inner
            .next()
            .map(|next| next.map(|value| E::read(self.replica, value)))
    }
}

impl<'a, E: Entity + EntityRead> EntityIter<'a, E> {
    /// Construct a new Iterator over the [`Entities`][`Entity`] in a
    /// [`Repository`].
    ///
    /// # Note
    /// You probably want to use the higher level
    /// [`Replica::get_all()`][`super::Replica::get_all`] method instead.
    pub(super) fn new(replica: &'a Replica) -> Result<Self, get::Error> {
        let inner = EntityIdIter::new(replica.repo())?;
        Ok(Self { replica, inner })
    }
}