git_bug/entities/identity/snapshot/
timeline.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//! Implementation of the [`Timeline`] trait for use in
12//! [`Identities`][`Identity`].
13
14use super::history_step::{
15    AvatarUrlHistoryStep, EmailHistoryStep, IdentityHistoryStep, LoginNameHistoryStep,
16    MetadataHistoryStep, NameHistoryStep,
17};
18use crate::{
19    entities::identity::{Identity, identity_operation::IdentityOperationData},
20    replica::entity::{Entity, operation::Operation, snapshot::timeline::Timeline},
21};
22
23/// The timeline of changes of this [`Identity`].
24#[derive(Debug, Clone)]
25pub struct IdentityTimeline {
26    history: Vec<IdentityHistoryStep>,
27}
28
29impl Timeline<Identity> for IdentityTimeline {
30    fn new() -> Self {
31        Self { history: vec![] }
32    }
33
34    fn from_root_operation(operation: &Operation<Identity>) -> Self {
35        let mut me = Self::new();
36
37        let IdentityOperationData::Create { name } = operation.operation_data() else {
38            unreachable!("This should only be called with the root operation.");
39        };
40        me.add_name_history(NameHistoryStep {
41            author: operation.author(),
42            name: name.to_owned(),
43            at: operation.creation_time(),
44        });
45
46        me
47    }
48
49    fn add(&mut self, op: &Operation<Identity>) {
50        match op.operation_data() {
51            IdentityOperationData::Create { .. } => unreachable!("Already processed."),
52            IdentityOperationData::SetName { name } => self.add_name_history(NameHistoryStep {
53                author: op.author(),
54                name: name.to_owned(),
55                at: op.creation_time(),
56            }),
57            IdentityOperationData::SetEmail { email } => self.add_email_history(EmailHistoryStep {
58                author: op.author(),
59                email: email.to_owned(),
60                at: op.creation_time(),
61            }),
62            IdentityOperationData::SetLoginName { login_name } => {
63                self.add_login_name_history(LoginNameHistoryStep {
64                    author: op.author(),
65                    login_name: login_name.to_owned(),
66                    at: op.creation_time(),
67                });
68            }
69            IdentityOperationData::SetAvatarUrl { url } => {
70                self.add_avatar_url_history(AvatarUrlHistoryStep {
71                    author: op.author(),
72                    avatar_url: url.to_owned(),
73                    at: op.creation_time(),
74                });
75            }
76            IdentityOperationData::SetMetadata { metadata } => {
77                self.add_metadata_history(MetadataHistoryStep {
78                    author: op.author(),
79                    metadata: metadata.to_owned(),
80                    at: op.creation_time(),
81                });
82            }
83        }
84    }
85
86    fn history(&self) -> &[<Identity as Entity>::HistoryStep] {
87        &self.history
88    }
89}
90
91macro_rules! filter_history {
92    ($history:expr, $type_name:ident) => {
93        filter_history!(@iter $history, $type_name)
94    };
95    (@$mode:ident $history:expr, $type_name:ident) => {
96        $history.$mode().filter_map(|h| {
97            if let IdentityHistoryStep::$type_name(a) = h {
98                Some(a)
99            } else {
100                None
101            }
102        })
103    };
104}
105
106impl IdentityTimeline {
107    /// Return an iterator over the [`NameHistorySteps`][`NameHistoryStep`].
108    pub fn name_history(&self) -> impl Iterator<Item = &NameHistoryStep> {
109        filter_history!(self.history, Name)
110    }
111
112    /// Return an iterator over the [`EmailHistorySteps`][`EmailHistoryStep`].
113    pub fn email_history(&self) -> impl Iterator<Item = &EmailHistoryStep> {
114        filter_history!(self.history, Email)
115    }
116
117    /// Return an iterator over the
118    /// [`LoginNameHistorySteps`][`LoginNameHistoryStep`].
119    pub fn login_name_history(&self) -> impl Iterator<Item = &LoginNameHistoryStep> {
120        filter_history!(self.history, LoginName)
121    }
122
123    /// Return an iterator over the
124    /// [`AvatarUrlHistorySteps`][`AvatarUrlHistoryStep`].
125    pub fn avatar_url_history(&self) -> impl Iterator<Item = &AvatarUrlHistoryStep> {
126        filter_history!(self.history, AvatarUrl)
127    }
128
129    /// Return an iterator over the
130    /// [`MetadataHistorySteps`][`MetadataHistoryStep`].
131    pub fn metadata_history(&self) -> impl Iterator<Item = &MetadataHistoryStep> {
132        filter_history!(self.history, Metadata)
133    }
134
135    // Mutating
136
137    /// Add an [`NameHistoryStep`] to this [`Timeline`].
138    pub fn add_name_history(&mut self, item: NameHistoryStep) {
139        self.history.push(IdentityHistoryStep::Name(item));
140    }
141
142    /// Add an [`EmailHistoryStep`] to this [`Timeline`].
143    pub fn add_email_history(&mut self, item: EmailHistoryStep) {
144        self.history.push(IdentityHistoryStep::Email(item));
145    }
146
147    /// Add an [`LoginNameHistoryStep`] to this [`Timeline`].
148    pub fn add_login_name_history(&mut self, item: LoginNameHistoryStep) {
149        self.history.push(IdentityHistoryStep::LoginName(item));
150    }
151
152    /// Add an [`AvatarUrlHistoryStep`] to this [`Timeline`].
153    pub fn add_avatar_url_history(&mut self, item: AvatarUrlHistoryStep) {
154        self.history.push(IdentityHistoryStep::AvatarUrl(item));
155    }
156
157    /// Add an [`MetadataHistoryStep`] to this [`Timeline`].
158    pub fn add_metadata_history(&mut self, item: MetadataHistoryStep) {
159        self.history.push(IdentityHistoryStep::Metadata(item));
160    }
161}