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

//! Handling for the Nonce value used to make each
//! [`Operation`][`crate::replica::entity::operation::Operation`] unique.

use base64::{Engine, prelude::BASE64_STANDARD};
use serde::{Deserialize, Serialize};

/// A garbage value, to ensure unique IDs.
/// Git-bug creates nonces from 20 < len < 64.
/// Thus store 64 bytes, as this will always suffice.
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct Nonce {
    // HACK(@bpeetz): We split the [_;64] up into two parts, as serde does not support arrays
    // longer than 64 elements. <2025-06-06>
    first: [Option<u8>; 32],
    second: [Option<u8>; 32],
}

impl TryFrom<String> for Nonce {
    type Error = base64::DecodeSliceError;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        <Self as TryFrom<&str>>::try_from(&value)
    }
}
impl TryFrom<&str> for Nonce {
    type Error = base64::DecodeSliceError;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        // The Nonce is base64 encoded

        let mut buffer = [0; 64];

        let written = BASE64_STANDARD.decode_slice(value, &mut buffer)?;

        let mut output = [None; 64];
        for (slot, byte) in output.iter_mut().zip(buffer[..written].iter()) {
            *slot = Some(*byte);
        }

        let mut first = [None; 32];
        first.copy_from_slice(&output[..32]);

        let mut second = [None; 32];
        second.copy_from_slice(&output[32..]);

        Ok(Self { first, second })
    }
}

impl From<Nonce> for String {
    fn from(value: Nonce) -> Self {
        let mut buffer = String::new();

        let mut index = 0;
        let mut payload = [0; 64];
        for (slot, maybe_byte) in payload
            .iter_mut()
            .zip(value.first.iter().chain(value.second.iter()))
        {
            if let Some(byte) = maybe_byte {
                *slot = *byte;
                index += 1;
            }
        }

        let trimmed_payload = &payload[..index];

        BASE64_STANDARD.encode_string(trimmed_payload, &mut buffer);
        buffer
    }
}