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#![expect(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 pollster::FutureExt as _;
27
28use crate::backend::CommitId;
29use crate::op_store;
30use crate::op_store::OpStore;
31use crate::op_store::OpStoreResult;
32use crate::op_store::OperationId;
33use crate::op_store::OperationMetadata;
34use crate::op_store::ViewId;
35use crate::view::View;
36
37/// A wrapper around [`op_store::Operation`] that defines additional methods and
38/// stores a pointer to the `OpStore` the operation belongs to.
39#[derive(Clone, serde::Serialize)]
40pub struct Operation {
41    #[serde(skip)]
42    op_store: Arc<dyn OpStore>,
43    id: OperationId,
44    #[serde(flatten)]
45    data: Arc<op_store::Operation>, // allow cheap clone
46}
47
48impl Debug for Operation {
49    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
50        f.debug_struct("Operation").field("id", &self.id).finish()
51    }
52}
53
54impl PartialEq for Operation {
55    fn eq(&self, other: &Self) -> bool {
56        self.id == other.id
57    }
58}
59
60impl Eq for Operation {}
61
62impl Ord for Operation {
63    fn cmp(&self, other: &Self) -> Ordering {
64        self.id.cmp(&other.id)
65    }
66}
67
68impl PartialOrd for Operation {
69    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
70        Some(self.cmp(other))
71    }
72}
73
74impl Hash for Operation {
75    fn hash<H: Hasher>(&self, state: &mut H) {
76        self.id.hash(state);
77    }
78}
79
80impl Operation {
81    pub fn new(
82        op_store: Arc<dyn OpStore>,
83        id: OperationId,
84        data: impl Into<Arc<op_store::Operation>>,
85    ) -> Self {
86        Self {
87            op_store,
88            id,
89            data: data.into(),
90        }
91    }
92
93    pub fn op_store(&self) -> Arc<dyn OpStore> {
94        self.op_store.clone()
95    }
96
97    pub fn id(&self) -> &OperationId {
98        &self.id
99    }
100
101    pub fn view_id(&self) -> &ViewId {
102        &self.data.view_id
103    }
104
105    pub fn parent_ids(&self) -> &[OperationId] {
106        &self.data.parents
107    }
108
109    pub fn parents(&self) -> impl ExactSizeIterator<Item = OpStoreResult<Self>> {
110        let op_store = &self.op_store;
111        self.data.parents.iter().map(|parent_id| {
112            let data = op_store.read_operation(parent_id).block_on()?;
113            Ok(Self::new(op_store.clone(), parent_id.clone(), data))
114        })
115    }
116
117    pub fn view(&self) -> OpStoreResult<View> {
118        let data = self.op_store.read_view(&self.data.view_id).block_on()?;
119        Ok(View::new(data))
120    }
121
122    pub fn metadata(&self) -> &OperationMetadata {
123        &self.data.metadata
124    }
125
126    /// Returns true if predecessors are recorded in this operation.
127    ///
128    /// This returns false only if the operation was written by jj < 0.30.
129    pub fn stores_commit_predecessors(&self) -> bool {
130        self.data.commit_predecessors.is_some()
131    }
132
133    /// Returns predecessors of the specified commit if recorded.
134    pub fn predecessors_for_commit(&self, commit_id: &CommitId) -> Option<&[CommitId]> {
135        let map = self.data.commit_predecessors.as_ref()?;
136        Some(map.get(commit_id)?)
137    }
138
139    /// Iterates all commit ids referenced by this operation ignoring the view.
140    ///
141    /// Use this in addition to [`View::all_referenced_commit_ids()`] to build
142    /// commit index from scratch. The predecessor commit ids are also included,
143    /// which ensures that the old commits to be returned by
144    /// [`Self::predecessors_for_commit()`] are still reachable.
145    ///
146    /// The iteration order is unspecified.
147    pub fn all_referenced_commit_ids(&self) -> impl Iterator<Item = &CommitId> {
148        self.data.commit_predecessors.iter().flat_map(|map| {
149            map.iter()
150                .flat_map(|(new_id, old_ids)| iter::once(new_id).chain(old_ids))
151        })
152    }
153
154    pub fn store_operation(&self) -> &op_store::Operation {
155        &self.data
156    }
157}