radicle_fetch/git/refs/
update.rs

1//! The set of types that describe performing updates to a Git
2//! repository.
3//!
4//! An [`Update`] describes a single update that can made to a Git
5//! repository. **Note** that it currently does not support symbolic
6//! references.
7//!
8//! A group of `Update`s is described by [`Updates`] which groups
9//! those updates by each peer's namespace, i.e. their [`PublicKey`].
10//!
11//! When an `Update` is successful the corresponding [`Updated`] is
12//! expected to be produced.
13//!
14//! The final result of applying a set of [`Updates`] is captured in
15//! the [`Applied`] type, which contains any rejected, but non-fatal,
16//! [`Update`]s and successful [`Updated`] values.
17
18use std::collections::BTreeMap;
19
20use either::Either;
21use radicle::git::{Namespaced, Oid, Qualified};
22use radicle::prelude::PublicKey;
23
24pub use radicle::storage::RefUpdate;
25
26/// The set of applied changes from a reference store update.
27#[derive(Debug, Default)]
28pub struct Applied<'a> {
29    /// Set of rejected updates if they did not meet the update
30    /// requirements, e.g. concurrent change to previous object id,
31    /// broke fast-forward policy, etc.
32    pub rejected: Vec<Update<'a>>,
33    /// Set of successfully updated references.
34    pub updated: Vec<RefUpdate>,
35}
36
37impl Applied<'_> {
38    pub fn append(&mut self, other: &mut Self) {
39        self.rejected.append(&mut other.rejected);
40        self.updated.append(&mut other.updated);
41    }
42}
43
44/// A set of [`Update`]s that are grouped by which namespace they are
45/// affecting.
46#[derive(Clone, Default, Debug)]
47pub struct Updates<'a> {
48    pub tips: BTreeMap<PublicKey, Vec<Update<'a>>>,
49}
50
51impl<'a> Updates<'a> {
52    pub fn build(updates: impl IntoIterator<Item = (PublicKey, Update<'a>)>) -> Self {
53        let tips = updates.into_iter().fold(
54            BTreeMap::<_, Vec<Update<'a>>>::new(),
55            |mut tips, (remote, up)| {
56                tips.entry(remote)
57                    .and_modify(|ups| ups.push(up.clone()))
58                    .or_insert(vec![up]);
59                tips
60            },
61        );
62        Self { tips }
63    }
64
65    pub fn add(&mut self, remote: PublicKey, up: Update<'a>) {
66        self.tips
67            .entry(remote)
68            .and_modify(|ups| ups.push(up.clone()))
69            .or_insert(vec![up]);
70    }
71
72    pub fn append(&mut self, remote: PublicKey, mut new: Vec<Update<'a>>) {
73        self.tips
74            .entry(remote)
75            .and_modify(|ups| ups.append(&mut new))
76            .or_insert(new);
77    }
78}
79
80/// The policy to follow when an [`Update::Direct`] is not a
81/// fast-forward.
82#[derive(Clone, Copy, Debug)]
83pub enum Policy {
84    /// Abort the entire transaction.
85    Abort,
86    /// Reject this update, but continue the transaction.
87    Reject,
88    /// Allow the update.
89    Allow,
90}
91
92/// An update that can be applied to a Git repository.
93#[derive(Clone, Debug)]
94pub enum Update<'a> {
95    /// Update a direct reference, i.e. a reference that points to an
96    /// object.
97    Direct {
98        /// The name of the reference that is being updated.
99        name: Namespaced<'a>,
100        /// The resulting target of the reference that is being
101        /// updated.
102        target: Oid,
103        /// Policy to apply when an [`Update`] would not apply as a
104        /// fast-forward.
105        no_ff: Policy,
106    },
107    /// Delete a reference.
108    Prune {
109        /// The name of the reference that is being deleted.
110        name: Namespaced<'a>,
111        /// The previous value of the reference.
112        ///
113        /// It can either be a direct reference pointing to an
114        /// [`Oid`], or a symbolic reference pointing to a
115        /// [`Qualified`] reference name.
116        prev: Either<Oid, Qualified<'a>>,
117    },
118}
119
120impl<'a> Update<'a> {
121    pub fn refname(&self) -> &Namespaced<'a> {
122        match self {
123            Update::Direct { name, .. } => name,
124            Update::Prune { name, .. } => name,
125        }
126    }
127
128    pub fn into_owned<'b>(self) -> Update<'b> {
129        match self {
130            Self::Direct {
131                name,
132                target,
133                no_ff,
134            } => Update::Direct {
135                name: name.into_owned(),
136                target,
137                no_ff,
138            },
139            Self::Prune { name, prev } => Update::Prune {
140                name: name.into_owned(),
141                prev: prev.map_right(|q| q.into_owned()),
142            },
143        }
144    }
145}