git_bug/replica/entity/operation/
operation_data.rs

1// git-bug-rs - A rust library for interfacing with git-bug repositories
2//
3// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
4// SPDX-License-Identifier: GPL-3.0-or-later
5//
6// This file is part of git-bug-rs/git-gub.
7//
8// You should have received a copy of the License along with this program.
9// If not, see <https://www.gnu.org/licenses/agpl.txt>.
10
11//! Handle [`Operations`][`super::Operation`].
12
13/// A helper macro for working with the (owned) [`simd_json::owned::Value`].
14///
15/// This is mostly here to make the Value decoding code in the entities/ subdirectory easier.
16#[macro_export]
17macro_rules! get {
18    ($object:expr, $field_name:literal, $as_convert:ident, $error:path) => {{
19        use $error;
20
21        get! {@option $object, $field_name, $as_convert, $error}
22            .ok_or_else(|| Error::MissingJsonField { field: $field_name })?
23    }};
24
25    (@option[next] $object:expr, $field_name:literal, $next:expr, $error:path) => {{
26        use simd_json::derived::TypedScalarValue;
27
28        if let Some(some) = $object.remove($field_name) {
29            if some.is_null() { None } else { $next(some)? }
30        } else {
31            None
32        }
33    }};
34
35    (@option $object:expr, $field_name:literal, $as_convert:ident, $error:path) => {{
36        get! {@option[next] $object, $field_name, |some: simd_json::owned::Value| {
37            use $error;
38
39            Ok::<_, Error>(Some(some.$as_convert().map_err(|err| Error::WrongJsonType {
40                err,
41                field: $field_name,
42            })?))
43        }, $error}
44    }};
45
46    (@map[preserve - order] $object:expr, $field_name:literal, $as_convert:ident, $error:path) => {{
47        use simd_json::derived::ValueTryIntoObject;
48
49        let base = get! {$object, $field_name, try_into_object, $error};
50
51        get! {@mk_map base, $as_convert, $error}
52    }};
53
54    (@mk_map $object:expr, $as_convert:ident, $error:path) => {{
55        use $error;
56
57        $object
58            .into_iter()
59            .map(|(key, nested_value)| {
60                Ok::<_, Error>((
61                    key,
62                    nested_value
63                        .$as_convert()
64                        .map_err(|err| Error::WrongJsonType {
65                            err,
66                            field: "metadata field",
67                        })?,
68                ))
69            })
70            .collect::<Result<_, _>>()?
71    }};
72}
73pub(crate) use get;
74
75/// The necessary accessors needed to connect
76/// an [`Entity::OperationData`][`super::Entity::OperationData`] to an
77/// [`Operations`][`super::operations::Operations`] structure.
78pub trait OperationData {
79    /// The returned error, when the operation data cannot be parsed from the
80    /// stored JSON value.
81    type DecodeError: std::fmt::Debug + std::fmt::Display + std::error::Error;
82
83    /// Whether this operation is a root operation.
84    ///
85    /// Root operations are the operations that create an [`Entity`][`super::Entity`].
86    /// As such, [`Operations`][`super::operations::Operations`] ensures that
87    /// *exactly one* of these root operations
88    /// exists in the operations composing an [`Entity`][`super::Entity`].
89    /// Furthermore, it also asserts, that this root operation is actually the
90    /// first operation.
91    fn is_root(&self) -> bool;
92
93    /// Parses the stored JSON object as operation data.
94    ///
95    /// This function takes the JSON value and the type of the operation data,
96    /// which has been extracted from the JSON value.
97    ///
98    /// # Errors
99    /// If the raw json value cannot be parsed as a Operation.
100    fn from_value(
101        raw: simd_json::owned::Value,
102        predicted_type: u64,
103    ) -> Result<Self, Self::DecodeError>
104    where
105        Self: Sized;
106
107    /// Encodes this operation data in it's JSON representation.
108    fn as_value(&self) -> simd_json::borrowed::Object<'_>;
109
110    /// Get an unique integer for each valid operation.
111    ///
112    /// This is needed to tag the JSON value.
113    fn to_json_type(&self) -> u64;
114}