git_bug/entities/identity/identity_operation/
mod.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//! Operations that affect an [`Identity`][`super::Identity`]
12
13use serde::{Deserialize, Serialize};
14use simd_json::{derived::ValueTryIntoObject, owned};
15use url::Url;
16
17use self::identity_operation_type::IdentityOperationType;
18use crate::replica::entity::operation::operation_data::OperationData;
19
20pub mod identity_operation_type;
21mod op;
22
23/// The operations available on an [`Identity`][`super::Identity`].
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub enum IdentityOperationData {
26    /// Creating an user.
27    Create {
28        /// The name of this user.
29        name: String,
30    },
31
32    /// Setting the user's name.
33    SetName {
34        /// The new name.
35        name: String,
36    },
37
38    /// Setting the user's email address.
39    SetEmail {
40        /// The new email address.
41        email: String,
42    },
43
44    /// Setting the user's login name.
45    SetLoginName {
46        /// The name used to login to a bridge.
47        // TODO(@bpeetz): Why are we storing this in that way? Shouldn't this name be associated
48        // whit a platform or a source URL. And why is that not metadata, like the rest of the
49        // bridge data? <2025-04-20>
50        login_name: String,
51    },
52
53    /// Setting the URL to the user's avatar.
54    SetAvatarUrl {
55        /// The new avatar URL.
56        url: Url,
57    },
58
59    /// Setting metadata.
60    SetMetadata {
61        /// Which metadata to add.
62        // Use a vec here to preserve the ordering.
63        metadata: Vec<(String, String)>,
64    },
65    // TODO: Support pgp keys <2025-04-20>
66    // AddKey {
67    //     key: Key,
68    // },
69    // RemoveKey {
70    //     key: Key,
71    // },
72}
73
74impl OperationData for IdentityOperationData {
75    type DecodeError = decode::Error;
76
77    fn is_root(&self) -> bool {
78        matches!(self, Self::Create { .. })
79    }
80
81    fn from_value(raw: owned::Value, predicted_type: u64) -> Result<Self, Self::DecodeError>
82    where
83        Self: Sized,
84    {
85        let r#type = IdentityOperationType::try_from(predicted_type)?;
86
87        let object = raw
88            .try_into_object()
89            .map_err(|err| decode::Error::ObjectExpected { err })?;
90
91        match r#type {
92            IdentityOperationType::Create => op::create(object),
93            IdentityOperationType::SetName => op::set_name(object),
94            IdentityOperationType::SetEmail => op::set_email(object),
95            IdentityOperationType::SetLoginName => op::set_login_name(object),
96            IdentityOperationType::SetAvatarUrl => op::set_avatar_url(object),
97            IdentityOperationType::SetMetadata => op::set_metadata(object),
98        }
99    }
100
101    fn as_value(&self) -> simd_json::borrowed::Object<'_> {
102        match self {
103            IdentityOperationData::Create { name } => op::create_value(name),
104            IdentityOperationData::SetName { name } => op::set_name_value(name),
105            IdentityOperationData::SetEmail { email } => op::set_email_value(email),
106            IdentityOperationData::SetLoginName { login_name } => {
107                op::set_login_name_value(login_name)
108            }
109            IdentityOperationData::SetAvatarUrl { url } => op::set_avatar_url_value(url),
110            IdentityOperationData::SetMetadata { metadata } => op::set_metadata_value(metadata),
111        }
112    }
113
114    fn to_json_type(&self) -> u64 {
115        IdentityOperationType::from(self).into()
116    }
117}
118
119#[allow(missing_docs)]
120pub mod decode {
121
122    use super::identity_operation_type;
123
124    #[derive(Debug, thiserror::Error)]
125    pub enum Error {
126        #[error(transparent)]
127        InvalidType(#[from] identity_operation_type::decode::Error),
128
129        #[error("The json value did not contain the expected field '{field}'.")]
130        MissingJsonField { field: &'static str },
131
132        #[error("Expected to parse the '{field}' field in the json data: {err}")]
133        WrongJsonType {
134            err: simd_json::TryTypeError,
135            field: &'static str,
136        },
137
138        #[error("Failed to parse an url value: {0}")]
139        UrlParse(#[from] url::ParseError),
140
141        #[error("Expected this operation json data to be a object, but was not: {err}")]
142        ObjectExpected { err: simd_json::TryTypeError },
143    }
144}