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

//! Handle [`Operations`][`super::Operation`].

/// A helper macro for working with the (owned) [`simd_json::owned::Value`].
///
/// This is mostly here to make the Value decoding code in the entities/ subdirectory easier.
#[macro_export]
macro_rules! get {
    ($object:expr, $field_name:literal, $as_convert:ident, $error:path) => {{
        use $error;

        get! {@option $object, $field_name, $as_convert, $error}
            .ok_or_else(|| Error::MissingJsonField { field: $field_name })?
    }};

    (@option[next] $object:expr, $field_name:literal, $next:expr, $error:path) => {{
        use simd_json::derived::TypedScalarValue;

        if let Some(some) = $object.remove($field_name) {
            if some.is_null() { None } else { $next(some)? }
        } else {
            None
        }
    }};

    (@option $object:expr, $field_name:literal, $as_convert:ident, $error:path) => {{
        get! {@option[next] $object, $field_name, |some: simd_json::owned::Value| {
            use $error;

            Ok::<_, Error>(Some(some.$as_convert().map_err(|err| Error::WrongJsonType {
                err,
                field: $field_name,
            })?))
        }, $error}
    }};

    (@map[preserve - order] $object:expr, $field_name:literal, $as_convert:ident, $error:path) => {{
        use simd_json::derived::ValueTryIntoObject;

        let base = get! {$object, $field_name, try_into_object, $error};

        get! {@mk_map base, $as_convert, $error}
    }};

    (@mk_map $object:expr, $as_convert:ident, $error:path) => {{
        use $error;

        $object
            .into_iter()
            .map(|(key, nested_value)| {
                Ok::<_, Error>((
                    key,
                    nested_value
                        .$as_convert()
                        .map_err(|err| Error::WrongJsonType {
                            err,
                            field: "metadata field",
                        })?,
                ))
            })
            .collect::<Result<_, _>>()?
    }};
}
pub(crate) use get;

/// The necessary accessors needed to connect
/// an [`Entity::OperationData`][`super::Entity::OperationData`] to an
/// [`Operations`][`super::operations::Operations`] structure.
pub trait OperationData {
    /// The returned error, when the operation data cannot be parsed from the
    /// stored JSON value.
    type DecodeError: std::fmt::Debug + std::fmt::Display + std::error::Error;

    /// Whether this operation is a root operation.
    ///
    /// Root operations are the operations that create an [`Entity`][`super::Entity`].
    /// As such, [`Operations`][`super::operations::Operations`] ensures that
    /// *exactly one* of these root operations
    /// exists in the operations composing an [`Entity`][`super::Entity`].
    /// Furthermore, it also asserts, that this root operation is actually the
    /// first operation.
    fn is_root(&self) -> bool;

    /// Parses the stored JSON object as operation data.
    ///
    /// This function takes the JSON value and the type of the operation data,
    /// which has been extracted from the JSON value.
    ///
    /// # Errors
    /// If the raw json value cannot be parsed as a Operation.
    fn from_value(
        raw: simd_json::owned::Value,
        predicted_type: u64,
    ) -> Result<Self, Self::DecodeError>
    where
        Self: Sized;

    /// Encodes this operation data in it's JSON representation.
    fn as_value(&self) -> simd_json::borrowed::Object<'_>;

    /// Get an unique integer for each valid operation.
    ///
    /// This is needed to tag the JSON value.
    fn to_json_type(&self) -> u64;
}