jujutsu_lib/
legacy_thrift_op_store.rs

1// Copyright 2022 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
15use std::collections::BTreeMap;
16use std::fmt::Debug;
17use std::fs::File;
18use std::io::{ErrorKind, Read};
19use std::path::PathBuf;
20
21use itertools::Itertools;
22use thrift::protocol::{TCompactInputProtocol, TSerializable};
23
24use crate::backend::{CommitId, MillisSinceEpoch, ObjectId, Timestamp};
25use crate::op_store::{
26    BranchTarget, OpStoreError, OpStoreResult, Operation, OperationId, OperationMetadata,
27    RefTarget, View, ViewId, WorkspaceId,
28};
29use crate::simple_op_store_model;
30
31impl From<thrift::Error> for OpStoreError {
32    fn from(err: thrift::Error) -> Self {
33        OpStoreError::Other(err.to_string())
34    }
35}
36
37fn not_found_to_store_error(err: std::io::Error) -> OpStoreError {
38    if err.kind() == ErrorKind::NotFound {
39        OpStoreError::NotFound
40    } else {
41        OpStoreError::from(err)
42    }
43}
44
45#[derive(Debug)]
46pub struct ThriftOpStore {
47    path: PathBuf,
48}
49
50impl ThriftOpStore {
51    pub fn load(store_path: PathBuf) -> Self {
52        ThriftOpStore { path: store_path }
53    }
54
55    fn view_path(&self, id: &ViewId) -> PathBuf {
56        self.path.join("views").join(id.hex())
57    }
58
59    fn operation_path(&self, id: &OperationId) -> PathBuf {
60        self.path.join("operations").join(id.hex())
61    }
62
63    pub fn read_view(&self, id: &ViewId) -> OpStoreResult<View> {
64        let path = self.view_path(id);
65        let mut file = File::open(path).map_err(not_found_to_store_error)?;
66        let thrift_view = read_thrift(&mut file)?;
67        Ok(View::from(&thrift_view))
68    }
69
70    pub fn read_operation(&self, id: &OperationId) -> OpStoreResult<Operation> {
71        let path = self.operation_path(id);
72        let mut file = File::open(path).map_err(not_found_to_store_error)?;
73        let thrift_operation = read_thrift(&mut file)?;
74        Ok(Operation::from(&thrift_operation))
75    }
76}
77
78pub fn read_thrift<T: TSerializable>(input: &mut impl Read) -> OpStoreResult<T> {
79    let mut protocol = TCompactInputProtocol::new(input);
80    Ok(TSerializable::read_from_in_protocol(&mut protocol).unwrap())
81}
82
83impl From<&simple_op_store_model::Timestamp> for Timestamp {
84    fn from(timestamp: &simple_op_store_model::Timestamp) -> Self {
85        Timestamp {
86            timestamp: MillisSinceEpoch(timestamp.millis_since_epoch),
87            tz_offset: timestamp.tz_offset,
88        }
89    }
90}
91
92impl From<&simple_op_store_model::OperationMetadata> for OperationMetadata {
93    fn from(metadata: &simple_op_store_model::OperationMetadata) -> Self {
94        let start_time = Timestamp::from(&metadata.start_time);
95        let end_time = Timestamp::from(&metadata.end_time);
96        let description = metadata.description.to_owned();
97        let hostname = metadata.hostname.to_owned();
98        let username = metadata.username.to_owned();
99        let tags = metadata
100            .tags
101            .iter()
102            .map(|(key, value)| (key.clone(), value.clone()))
103            .collect();
104        OperationMetadata {
105            start_time,
106            end_time,
107            description,
108            hostname,
109            username,
110            tags,
111        }
112    }
113}
114
115impl From<&simple_op_store_model::Operation> for Operation {
116    fn from(operation: &simple_op_store_model::Operation) -> Self {
117        let operation_id_from_thrift = |parent: &Vec<u8>| OperationId::new(parent.clone());
118        let parents = operation
119            .parents
120            .iter()
121            .map(operation_id_from_thrift)
122            .collect();
123        let view_id = ViewId::new(operation.view_id.clone());
124        let metadata = OperationMetadata::from(operation.metadata.as_ref());
125        Operation {
126            view_id,
127            parents,
128            metadata,
129        }
130    }
131}
132
133impl From<&simple_op_store_model::View> for View {
134    fn from(thrift_view: &simple_op_store_model::View) -> Self {
135        let mut view = View::default();
136        for (workspace_id, commit_id) in &thrift_view.wc_commit_ids {
137            view.wc_commit_ids.insert(
138                WorkspaceId::new(workspace_id.clone()),
139                CommitId::new(commit_id.clone()),
140            );
141        }
142        for head_id_bytes in &thrift_view.head_ids {
143            view.head_ids.insert(CommitId::from_bytes(head_id_bytes));
144        }
145        for head_id_bytes in &thrift_view.public_head_ids {
146            view.public_head_ids
147                .insert(CommitId::from_bytes(head_id_bytes));
148        }
149
150        for thrift_branch in &thrift_view.branches {
151            let local_target = thrift_branch.local_target.as_ref().map(RefTarget::from);
152
153            let mut remote_targets = BTreeMap::new();
154            for remote_branch in &thrift_branch.remote_branches {
155                remote_targets.insert(
156                    remote_branch.remote_name.clone(),
157                    RefTarget::from(&remote_branch.target),
158                );
159            }
160
161            view.branches.insert(
162                thrift_branch.name.clone(),
163                BranchTarget {
164                    local_target,
165                    remote_targets,
166                },
167            );
168        }
169
170        for thrift_tag in &thrift_view.tags {
171            view.tags
172                .insert(thrift_tag.name.clone(), RefTarget::from(&thrift_tag.target));
173        }
174
175        for git_ref in &thrift_view.git_refs {
176            view.git_refs
177                .insert(git_ref.name.clone(), RefTarget::from(&git_ref.target));
178        }
179
180        view.git_head = thrift_view
181            .git_head
182            .as_ref()
183            .map(|head| RefTarget::Normal(CommitId::new(head.clone())));
184
185        view
186    }
187}
188
189impl From<&simple_op_store_model::RefTarget> for RefTarget {
190    fn from(thrift_ref_target: &simple_op_store_model::RefTarget) -> Self {
191        match thrift_ref_target {
192            simple_op_store_model::RefTarget::CommitId(commit_id) => {
193                RefTarget::Normal(CommitId::from_bytes(commit_id))
194            }
195            simple_op_store_model::RefTarget::Conflict(conflict) => {
196                let removes = conflict
197                    .removes
198                    .iter()
199                    .map(|id_bytes| CommitId::from_bytes(id_bytes))
200                    .collect_vec();
201                let adds = conflict
202                    .adds
203                    .iter()
204                    .map(|id_bytes| CommitId::from_bytes(id_bytes))
205                    .collect_vec();
206                RefTarget::Conflict { removes, adds }
207            }
208        }
209    }
210}