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}