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

//! Lamport Clocks scoped to a git [`repository`][`gix::Repository`].

use std::{fs, path::PathBuf};

use gix::Repository;

use super::persistent::PersistedClock;

#[allow(missing_docs)]
pub mod get {
    use crate::replica::entity::lamport;

    #[derive(Debug, thiserror::Error)]
    pub enum Error {
        #[error("Failed to read a clock named '{name}' from repo, because of {error}")]
        LoadClock {
            name: String,
            error: lamport::persistent::write::Error,
        },

        #[error("Failed to created a clock named '{name}' from repo, because of {error}")]
        CreateClock {
            name: String,
            error: lamport::persistent::write::Error,
        },

        #[error("Failed to create the directory for the repository clocks at the well known path.")]
        ClockPathCreate(std::io::Error),
    }
}

const SUBDIR_NAME: &str = "git-bug/clocks";

fn clock_path(repo: &Repository, name: &str) -> PathBuf {
    repo.path().join(SUBDIR_NAME).join(name)
}

/// Get a [`clock`][`PersistedClock`], which is scoped to this repository by
/// name. If the clock does not exist, [`None`][`Option::None`] is returned.
///
/// # Errors
/// If the [`clock`][`PersistedClock`] could not be loaded from disk.
pub fn get_clock(repo: &Repository, name: &str) -> Result<Option<PersistedClock>, get::Error> {
    let clock_path = clock_path(repo, name);

    if clock_path.exists() {
        let clock = PersistedClock::from_path(clock_path).map_err(|err| get::Error::LoadClock {
            name: name.to_owned(),
            error: err,
        })?;

        Ok(Some(clock))
    } else {
        Ok(None)
    }
}

/// Get a [`clock`][`PersistedClock`] from a repository by name.
/// Instead of [`get_clock`], this function will create a new clock if it was
/// not previously initialized.
///
/// # Errors
/// If the [`clock`][`PersistedClock`] could not be created or loaded from disk.
// Only expects.
#[allow(clippy::missing_panics_doc)]
pub fn get_or_init_clock(repo: &Repository, name: &str) -> Result<PersistedClock, get::Error> {
    if let Some(maybe_clock) = get_clock(repo, name)? {
        Ok(maybe_clock)
    } else {
        let clock_path = clock_path(repo, name);

        fs::create_dir_all(clock_path.parent().expect("Always has one"))
            .map_err(get::Error::ClockPathCreate)?;

        let new_clock = PersistedClock::new(clock_path).map_err(|err| get::Error::CreateClock {
            name: name.to_owned(),
            error: err,
        })?;

        Ok(new_clock)
    }
}