jj_lib/
operation.rs

1// Copyright 2020 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![allow(missing_docs)]
16
17use std::cmp::Ordering;
18use std::fmt::Debug;
19use std::fmt::Error;
20use std::fmt::Formatter;
21use std::hash::Hash;
22use std::hash::Hasher;
23use std::iter;
24use std::sync::Arc;
25
26use crate::backend::CommitId;
27use crate::op_store;
28use crate::op_store::OpStore;
29use crate::op_store::OpStoreResult;
30use crate::op_store::OperationId;
31use crate::op_store::OperationMetadata;
32use crate::op_store::ViewId;
33use crate::view::View;
34
35/// A wrapper around [`op_store::Operation`] that defines additional methods and
36/// stores a pointer to the `OpStore` the operation belongs to.
37#[derive(Clone, serde::Serialize)]
38pub struct Operation {
39    #[serde(skip)]
40    op_store: Arc<dyn OpStore>,
41    id: OperationId,
42    #[serde(flatten)]
43    data: Arc<op_store::Operation>, // allow cheap clone
44}
45
46impl Debug for Operation {
47    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
48        f.debug_struct("Operation").field("id", &self.id).finish()
49    }
50}
51
52impl PartialEq for Operation {
53    fn eq(&self, other: &Self) -> bool {
54        self.id == other.id
55    }
56}
57
58impl Eq for Operation {}
59
60impl Ord for Operation {
61    fn cmp(&self, other: &Self) -> Ordering {
62        self.id.cmp(&other.id)
63    }
64}
65
66impl PartialOrd for Operation {
67    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
68        Some(self.cmp(other))
69    }
70}
71
72impl Hash for Operation {
73    fn hash<H: Hasher>(&self, state: &mut H) {
74        self.id.hash(state);
75    }
76}
77
78impl Operation {
79    pub fn new(
80        op_store: Arc<dyn OpStore>,
81        id: OperationId,
82        data: impl Into<Arc<op_store::Operation>>,
83    ) -> Self {
84        Self {
85            op_store,
86            id,
87            data: data.into(),
88        }
89    }
90
91    pub fn op_store(&self) -> Arc<dyn OpStore> {
92        self.op_store.clone()
93    }
94
95    pub fn id(&self) -> &OperationId {
96        &self.id
97    }
98
99    pub fn view_id(&self) -> &ViewId {
100        &self.data.view_id
101    }
102
103    pub fn parent_ids(&self) -> &[OperationId] {
104        &self.data.parents
105    }
106
107    pub fn parents(&self) -> impl ExactSizeIterator<Item = OpStoreResult<Self>> + use<'_> {
108        let op_store = &self.op_store;
109        self.data.parents.iter().map(|parent_id| {
110            let data = op_store.read_operation(parent_id)?;
111            Ok(Self::new(op_store.clone(), parent_id.clone(), data))
112        })
113    }
114
115    pub fn view(&self) -> OpStoreResult<View> {
116        let data = self.op_store.read_view(&self.data.view_id)?;
117        Ok(View::new(data))
118    }
119
120    pub fn metadata(&self) -> &OperationMetadata {
121        &self.data.metadata
122    }
123
124    /// Returns true if predecessors are recorded in this operation.
125    ///
126    /// This returns false only if the operation was written by jj < 0.30.
127    pub fn stores_commit_predecessors(&self) -> bool {
128        self.data.commit_predecessors.is_some()
129    }
130
131    /// Returns predecessors of the specified commit if recorded.
132    pub fn predecessors_for_commit(&self, commit_id: &CommitId) -> Option<&[CommitId]> {
133        let map = self.data.commit_predecessors.as_ref()?;
134        Some(map.get(commit_id)?)
135    }
136
137    /// Iterates all commit ids referenced by this operation ignoring the view.
138    ///
139    /// Use this in addition to [`View::all_referenced_commit_ids()`] to build
140    /// commit index from scratch. The predecessor commit ids are also included,
141    /// which ensures that the old commits to be returned by
142    /// [`Self::predecessors_for_commit()`] are still reachable.
143    ///
144    /// The iteration order is unspecified.
145    pub fn all_referenced_commit_ids(&self) -> impl Iterator<Item = &CommitId> {
146        self.data.commit_predecessors.iter().flat_map(|map| {
147            map.iter()
148                .flat_map(|(new_id, old_ids)| iter::once(new_id).chain(old_ids))
149        })
150    }
151
152    pub fn store_operation(&self) -> &op_store::Operation {
153        &self.data
154    }
155}