lib: replace protobuf crate with prost

This commit is contained in:
Benjamin Saunders 2022-12-21 11:14:46 -08:00
parent 300f744e41
commit aaa175eca7
16 changed files with 702 additions and 287 deletions

View file

@ -42,6 +42,20 @@ jobs:
env:
RUST_BACKTRACE: 1
check-protos:
name: Check protos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b
- uses: dtolnay/rust-toolchain@e645b0cf01249a964ec099494d38d2da0f0b349f
with:
toolchain: stable
- run: sudo apt update && sudo apt-get -y install protobuf-compiler
- name: Generate Rust code from .proto files
run: cargo run -p gen-protos
- name: Check for uncommitted changes
run: git diff --exit-code
rustfmt:
name: Check formatting
runs-on: ubuntu-latest

111
Cargo.lock generated
View file

@ -545,6 +545,12 @@ dependencies = [
"instant",
]
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "float-cmp"
version = "0.9.0"
@ -564,6 +570,13 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "gen-protos"
version = "0.1.0"
dependencies = [
"prost-build",
]
[[package]]
name = "generic-array"
version = "0.14.6"
@ -830,8 +843,7 @@ dependencies = [
"once_cell",
"pest",
"pest_derive",
"protobuf",
"protobuf-codegen",
"prost",
"rand",
"regex",
"serde_json",
@ -984,6 +996,12 @@ dependencies = [
"windows-sys 0.36.1",
]
[[package]]
name = "multimap"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[package]]
name = "nom"
version = "7.1.1"
@ -1180,6 +1198,16 @@ dependencies = [
"sha1",
]
[[package]]
name = "petgraph"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
@ -1256,6 +1284,16 @@ dependencies = [
"termtree",
]
[[package]]
name = "prettyplease"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8992a85d8e93a28bdf76137db888d3874e3b230dee5ed8bebac4c9f7617773"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -1290,55 +1328,58 @@ dependencies = [
]
[[package]]
name = "protobuf"
version = "3.2.0"
name = "prost"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e"
checksum = "c01db6702aa05baa3f57dec92b8eeeeb4cb19e894e73996b32a4093289e54592"
dependencies = [
"bytes",
"once_cell",
"protobuf-support",
"thiserror",
"prost-derive",
]
[[package]]
name = "protobuf-codegen"
version = "3.2.0"
name = "prost-build"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901"
checksum = "cb5320c680de74ba083512704acb90fe00f28f79207286a848e730c45dd73ed6"
dependencies = [
"anyhow",
"once_cell",
"protobuf",
"protobuf-parse",
"regex",
"tempfile",
"thiserror",
]
[[package]]
name = "protobuf-parse"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49"
dependencies = [
"anyhow",
"indexmap",
"bytes",
"heck",
"itertools",
"lazy_static",
"log",
"protobuf",
"protobuf-support",
"multimap",
"petgraph",
"prettyplease",
"prost",
"prost-types",
"regex",
"syn",
"tempfile",
"thiserror",
"which",
]
[[package]]
name = "protobuf-support"
version = "3.2.0"
name = "prost-derive"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372"
checksum = "c8842bad1a5419bca14eac663ba798f6bc19c413c2fdceb5f3ba3b0932d96720"
dependencies = [
"thiserror",
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "prost-types"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "017f79637768cde62820bc2d4fe0e45daaa027755c323ad077767c6c5f173091"
dependencies = [
"bytes",
"prost",
]
[[package]]

View file

@ -31,7 +31,7 @@ name = "diff_bench"
harness = false
[workspace]
members = ["lib", "lib/testutils"]
members = ["lib", "lib/testutils", "lib/gen-protos"]
[dependencies]
chrono = { version = "0.4.23", default-features = false, features = ["std", "clock"] }

View file

@ -14,7 +14,6 @@ readme = "../README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
protobuf-codegen = "3.2.0"
version_check = "0.9.4"
[dependencies]
@ -32,7 +31,6 @@ maplit = "1.0.2"
once_cell = "1.16.0"
pest = "2.5.1"
pest_derive = "2.5.1"
protobuf = { version = "3.0.1", features = ["with-bytes"] }
regex = "1.7.0"
serde_json = "1.0.91"
tempfile = "3.3.0"
@ -42,6 +40,7 @@ uuid = { version = "1.2.2", features = ["v4"] }
whoami = "1.2.3"
zstd = "0.12.1"
tracing = "0.1.37"
prost = "0.11.5"
[dev-dependencies]
assert_matches = "1.5.0"

View file

@ -12,24 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
fn main() {
let input = &[
"src/protos/op_store.proto",
"src/protos/store.proto",
"src/protos/working_copy.proto",
];
protobuf_codegen::Codegen::new()
.pure()
.inputs(input)
.include("src/protos")
.cargo_out_dir("protos")
.run_from_script();
fn main() -> std::io::Result<()> {
println!("cargo:rerun-if-changed=build.rs");
for file in input {
println!("cargo:rerun-if-changed={file}");
}
if let Some(true) = version_check::supports_feature("map_first_last") {
println!("cargo:rustc-cfg=feature=\"map_first_last\"");
}
Ok(())
}

View file

@ -0,0 +1,8 @@
[package]
name = "gen-protos"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
prost-build = "0.11.5"

View file

@ -0,0 +1,34 @@
// Copyright 2022 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::io::Result;
use std::path::Path;
fn main() -> Result<()> {
let input = ["op_store.proto", "store.proto", "working_copy.proto"];
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
let protos_dir = root.join("src").join("protos");
prost_build::Config::new()
.out_dir(&protos_dir)
.include_file("mod.rs")
.compile_protos(
&input
.into_iter()
.map(|x| protos_dir.join(x))
.collect::<Vec<_>>(),
&[protos_dir],
)
}

View file

@ -20,7 +20,7 @@ use std::sync::{Arc, Mutex};
use git2::Oid;
use itertools::Itertools;
use protobuf::Message;
use prost::Message;
use uuid::Uuid;
use crate::backend::{
@ -126,17 +126,18 @@ fn signature_to_git(signature: &Signature) -> git2::Signature {
}
fn serialize_extras(commit: &Commit) -> Vec<u8> {
let mut proto = crate::protos::store::Commit::new();
proto.change_id = commit.change_id.to_bytes();
let mut proto = crate::protos::store::Commit {
change_id: commit.change_id.to_bytes(),
..Default::default()
};
for predecessor in &commit.predecessors {
proto.predecessors.push(predecessor.to_bytes());
}
proto.write_to_bytes().unwrap()
proto.encode_to_vec()
}
fn deserialize_extras(commit: &mut Commit, bytes: &[u8]) {
let mut cursor = Cursor::new(bytes);
let proto: crate::protos::store::Commit = Message::parse_from_reader(&mut cursor).unwrap();
let proto = crate::protos::store::Commit::decode(bytes).unwrap();
commit.change_id = ChangeId::new(proto.change_id);
for predecessor in &proto.predecessors {
commit.predecessors.push(CommitId::from_bytes(predecessor));

View file

@ -19,7 +19,7 @@ use std::io::{ErrorKind, Read, Write};
use std::path::{Path, PathBuf};
use blake2::{Blake2b512, Digest};
use protobuf::{Message, MessageField};
use prost::Message;
use tempfile::{NamedTempFile, PersistError};
use crate::backend::{
@ -43,8 +43,8 @@ impl From<PersistError> for BackendError {
}
}
impl From<protobuf::Error> for BackendError {
fn from(err: protobuf::Error) -> Self {
impl From<prost::DecodeError> for BackendError {
fn from(err: prost::DecodeError) -> Self {
BackendError::Other(err.to_string())
}
}
@ -183,17 +183,17 @@ impl Backend for LocalBackend {
fn read_tree(&self, _path: &RepoPath, id: &TreeId) -> BackendResult<Tree> {
let path = self.tree_path(id);
let mut file = File::open(path).map_err(not_found_to_backend_error)?;
let buf = fs::read(path).map_err(not_found_to_backend_error)?;
let proto: crate::protos::store::Tree = Message::parse_from_reader(&mut file)?;
Ok(tree_from_proto(&proto))
let proto = crate::protos::store::Tree::decode(&*buf)?;
Ok(tree_from_proto(proto))
}
fn write_tree(&self, _path: &RepoPath, tree: &Tree) -> BackendResult<TreeId> {
let temp_file = NamedTempFile::new_in(&self.path)?;
let proto = tree_to_proto(tree);
proto.write_to_writer(&mut temp_file.as_file())?;
temp_file.as_file().write_all(&proto.encode_to_vec())?;
let id = TreeId::new(blake2b_hash(tree).to_vec());
@ -203,17 +203,17 @@ impl Backend for LocalBackend {
fn read_conflict(&self, _path: &RepoPath, id: &ConflictId) -> BackendResult<Conflict> {
let path = self.conflict_path(id);
let mut file = File::open(path).map_err(not_found_to_backend_error)?;
let buf = fs::read(path).map_err(not_found_to_backend_error)?;
let proto: crate::protos::store::Conflict = Message::parse_from_reader(&mut file)?;
Ok(conflict_from_proto(&proto))
let proto = crate::protos::store::Conflict::decode(&*buf)?;
Ok(conflict_from_proto(proto))
}
fn write_conflict(&self, _path: &RepoPath, conflict: &Conflict) -> BackendResult<ConflictId> {
let temp_file = NamedTempFile::new_in(&self.path)?;
let proto = conflict_to_proto(conflict);
proto.write_to_writer(&mut temp_file.as_file())?;
temp_file.as_file().write_all(&proto.encode_to_vec())?;
let id = ConflictId::new(blake2b_hash(conflict).to_vec());
@ -227,17 +227,17 @@ impl Backend for LocalBackend {
}
let path = self.commit_path(id);
let mut file = File::open(path).map_err(not_found_to_backend_error)?;
let buf = fs::read(path).map_err(not_found_to_backend_error)?;
let proto: crate::protos::store::Commit = Message::parse_from_reader(&mut file)?;
Ok(commit_from_proto(&proto))
let proto = crate::protos::store::Commit::decode(&*buf)?;
Ok(commit_from_proto(proto))
}
fn write_commit(&self, commit: &Commit) -> BackendResult<CommitId> {
let temp_file = NamedTempFile::new_in(&self.path)?;
let proto = commit_to_proto(commit);
proto.write_to_writer(&mut temp_file.as_file())?;
temp_file.as_file().write_all(&proto.encode_to_vec())?;
let id = CommitId::new(blake2b_hash(commit).to_vec());
@ -247,7 +247,7 @@ impl Backend for LocalBackend {
}
pub fn commit_to_proto(commit: &Commit) -> crate::protos::store::Commit {
let mut proto = crate::protos::store::Commit::new();
let mut proto = crate::protos::store::Commit::default();
for parent in &commit.parents {
proto.parents.push(parent.to_bytes());
}
@ -257,115 +257,116 @@ pub fn commit_to_proto(commit: &Commit) -> crate::protos::store::Commit {
proto.root_tree = commit.root_tree.to_bytes();
proto.change_id = commit.change_id.to_bytes();
proto.description = commit.description.clone();
proto.author = MessageField::some(signature_to_proto(&commit.author));
proto.committer = MessageField::some(signature_to_proto(&commit.committer));
proto.author = Some(signature_to_proto(&commit.author));
proto.committer = Some(signature_to_proto(&commit.committer));
proto
}
fn commit_from_proto(proto: &crate::protos::store::Commit) -> Commit {
let commit_id_from_proto = |parent: &Vec<u8>| CommitId::new(parent.clone());
let parents = proto.parents.iter().map(commit_id_from_proto).collect();
let predecessors = proto
.predecessors
.iter()
.map(commit_id_from_proto)
.collect();
let root_tree = TreeId::new(proto.root_tree.to_vec());
let change_id = ChangeId::new(proto.change_id.to_vec());
fn commit_from_proto(proto: crate::protos::store::Commit) -> Commit {
let parents = proto.parents.into_iter().map(CommitId::new).collect();
let predecessors = proto.predecessors.into_iter().map(CommitId::new).collect();
let root_tree = TreeId::new(proto.root_tree);
let change_id = ChangeId::new(proto.change_id);
Commit {
parents,
predecessors,
root_tree,
change_id,
description: proto.description.clone(),
author: signature_from_proto(&proto.author),
committer: signature_from_proto(&proto.committer),
description: proto.description,
author: signature_from_proto(proto.author.unwrap_or_default()),
committer: signature_from_proto(proto.committer.unwrap_or_default()),
}
}
fn tree_to_proto(tree: &Tree) -> crate::protos::store::Tree {
let mut proto = crate::protos::store::Tree::new();
let mut proto = crate::protos::store::Tree::default();
for entry in tree.entries() {
let mut proto_entry = crate::protos::store::tree::Entry::new();
proto_entry.name = entry.name().string();
proto_entry.value = MessageField::some(tree_value_to_proto(entry.value()));
proto.entries.push(proto_entry);
proto.entries.push(crate::protos::store::tree::Entry {
name: entry.name().string(),
value: Some(tree_value_to_proto(entry.value())),
});
}
proto
}
fn tree_from_proto(proto: &crate::protos::store::Tree) -> Tree {
fn tree_from_proto(proto: crate::protos::store::Tree) -> Tree {
let mut tree = Tree::default();
for proto_entry in &proto.entries {
let value = tree_value_from_proto(proto_entry.value.as_ref().unwrap());
tree.set(RepoPathComponent::from(proto_entry.name.as_str()), value);
for proto_entry in proto.entries {
let value = tree_value_from_proto(proto_entry.value.unwrap());
tree.set(RepoPathComponent::from(proto_entry.name), value);
}
tree
}
fn tree_value_to_proto(value: &TreeValue) -> crate::protos::store::TreeValue {
let mut proto = crate::protos::store::TreeValue::new();
let mut proto = crate::protos::store::TreeValue::default();
match value {
TreeValue::File { id, executable } => {
let mut file = crate::protos::store::tree_value::File::new();
file.id = id.to_bytes();
file.executable = *executable;
proto.set_file(file);
proto.value = Some(crate::protos::store::tree_value::Value::File(
crate::protos::store::tree_value::File {
id: id.to_bytes(),
executable: *executable,
},
));
}
TreeValue::Symlink(id) => {
proto.set_symlink_id(id.to_bytes());
proto.value = Some(crate::protos::store::tree_value::Value::SymlinkId(
id.to_bytes(),
));
}
TreeValue::GitSubmodule(_id) => {
panic!("cannot store git submodules");
}
TreeValue::Tree(id) => {
proto.set_tree_id(id.to_bytes());
proto.value = Some(crate::protos::store::tree_value::Value::TreeId(
id.to_bytes(),
));
}
TreeValue::Conflict(id) => {
proto.set_conflict_id(id.to_bytes());
proto.value = Some(crate::protos::store::tree_value::Value::ConflictId(
id.to_bytes(),
));
}
}
proto
}
fn tree_value_from_proto(proto: &crate::protos::store::TreeValue) -> TreeValue {
match proto.value.as_ref().unwrap() {
crate::protos::store::tree_value::Value::TreeId(id) => {
TreeValue::Tree(TreeId::new(id.clone()))
}
fn tree_value_from_proto(proto: crate::protos::store::TreeValue) -> TreeValue {
match proto.value.unwrap() {
crate::protos::store::tree_value::Value::TreeId(id) => TreeValue::Tree(TreeId::new(id)),
crate::protos::store::tree_value::Value::File(crate::protos::store::tree_value::File {
id,
executable,
..
}) => TreeValue::File {
id: FileId::new(id.clone()),
executable: *executable,
id: FileId::new(id),
executable,
},
crate::protos::store::tree_value::Value::SymlinkId(id) => {
TreeValue::Symlink(SymlinkId::new(id.clone()))
TreeValue::Symlink(SymlinkId::new(id))
}
crate::protos::store::tree_value::Value::ConflictId(id) => {
TreeValue::Conflict(ConflictId::new(id.clone()))
TreeValue::Conflict(ConflictId::new(id))
}
}
}
fn signature_to_proto(signature: &Signature) -> crate::protos::store::commit::Signature {
let mut proto = crate::protos::store::commit::Signature::new();
proto.name = signature.name.clone();
proto.email = signature.email.clone();
let mut timestamp_proto = crate::protos::store::commit::Timestamp::new();
timestamp_proto.millis_since_epoch = signature.timestamp.timestamp.0;
timestamp_proto.tz_offset = signature.timestamp.tz_offset;
proto.timestamp = MessageField::some(timestamp_proto);
proto
crate::protos::store::commit::Signature {
name: signature.name.clone(),
email: signature.email.clone(),
timestamp: Some(crate::protos::store::commit::Timestamp {
millis_since_epoch: signature.timestamp.timestamp.0,
tz_offset: signature.timestamp.tz_offset,
}),
}
}
fn signature_from_proto(proto: &crate::protos::store::commit::Signature) -> Signature {
let timestamp = &proto.timestamp;
fn signature_from_proto(proto: crate::protos::store::commit::Signature) -> Signature {
let timestamp = proto.timestamp.unwrap_or_default();
Signature {
name: proto.name.clone(),
email: proto.email.clone(),
name: proto.name,
email: proto.email,
timestamp: Timestamp {
timestamp: MillisSinceEpoch(timestamp.millis_since_epoch),
tz_offset: timestamp.tz_offset,
@ -374,7 +375,7 @@ fn signature_from_proto(proto: &crate::protos::store::commit::Signature) -> Sign
}
fn conflict_to_proto(conflict: &Conflict) -> crate::protos::store::Conflict {
let mut proto = crate::protos::store::Conflict::new();
let mut proto = crate::protos::store::Conflict::default();
for part in &conflict.adds {
proto.adds.push(conflict_part_to_proto(part));
}
@ -384,25 +385,25 @@ fn conflict_to_proto(conflict: &Conflict) -> crate::protos::store::Conflict {
proto
}
fn conflict_from_proto(proto: &crate::protos::store::Conflict) -> Conflict {
fn conflict_from_proto(proto: crate::protos::store::Conflict) -> Conflict {
let mut conflict = Conflict::default();
for part in &proto.removes {
for part in proto.removes {
conflict.removes.push(conflict_part_from_proto(part))
}
for part in &proto.adds {
for part in proto.adds {
conflict.adds.push(conflict_part_from_proto(part))
}
conflict
}
fn conflict_part_from_proto(proto: &crate::protos::store::conflict::Part) -> ConflictPart {
fn conflict_part_from_proto(proto: crate::protos::store::conflict::Part) -> ConflictPart {
ConflictPart {
value: tree_value_from_proto(proto.content.as_ref().unwrap()),
value: tree_value_from_proto(proto.content.unwrap()),
}
}
fn conflict_part_to_proto(part: &ConflictPart) -> crate::protos::store::conflict::Part {
let mut proto = crate::protos::store::conflict::Part::new();
proto.content = MessageField::some(tree_value_to_proto(&part.value));
proto
crate::protos::store::conflict::Part {
content: Some(tree_value_to_proto(&part.value)),
}
}

View file

@ -15,12 +15,11 @@
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::fs;
use std::fs::File;
use std::io::ErrorKind;
use std::io::{ErrorKind, Write};
use std::path::PathBuf;
use itertools::Itertools;
use protobuf::{Message, MessageField};
use prost::Message;
use tempfile::NamedTempFile;
use crate::backend::{CommitId, MillisSinceEpoch, Timestamp};
@ -31,8 +30,8 @@ use crate::op_store::{
RefTarget, View, ViewId, WorkspaceId,
};
impl From<protobuf::Error> for OpStoreError {
fn from(err: protobuf::Error) -> Self {
impl From<prost::DecodeError> for OpStoreError {
fn from(err: prost::DecodeError) -> Self {
OpStoreError::Other(err.to_string())
}
}
@ -63,17 +62,17 @@ impl ProtoOpStore {
pub fn read_view(&self, id: &ViewId) -> OpStoreResult<View> {
let path = self.view_path(id);
let mut file = File::open(path).map_err(not_found_to_store_error)?;
let buf = fs::read(path)?;
let proto: crate::protos::op_store::View = Message::parse_from_reader(&mut file)?;
Ok(view_from_proto(&proto))
let proto = crate::protos::op_store::View::decode(&*buf)?;
Ok(view_from_proto(proto))
}
pub fn write_view(&self, view: &View) -> OpStoreResult<ViewId> {
let temp_file = NamedTempFile::new_in(&self.path)?;
let proto = view_to_proto(view);
proto.write_to_writer(&mut temp_file.as_file())?;
temp_file.as_file().write_all(&proto.encode_to_vec())?;
let id = ViewId::new(blake2b_hash(view).to_vec());
@ -83,17 +82,17 @@ impl ProtoOpStore {
pub fn read_operation(&self, id: &OperationId) -> OpStoreResult<Operation> {
let path = self.operation_path(id);
let mut file = File::open(path).map_err(not_found_to_store_error)?;
let buf = fs::read(path).map_err(not_found_to_store_error)?;
let proto: crate::protos::op_store::Operation = Message::parse_from_reader(&mut file)?;
Ok(operation_from_proto(&proto))
let proto = crate::protos::op_store::Operation::decode(&*buf)?;
Ok(operation_from_proto(proto))
}
pub fn write_operation(&self, operation: &Operation) -> OpStoreResult<OperationId> {
let temp_file = NamedTempFile::new_in(&self.path)?;
let proto = operation_to_proto(operation);
proto.write_to_writer(&mut temp_file.as_file())?;
temp_file.as_file().write_all(&proto.encode_to_vec())?;
let id = OperationId::new(blake2b_hash(operation).to_vec());
@ -111,13 +110,13 @@ fn not_found_to_store_error(err: std::io::Error) -> OpStoreError {
}
fn timestamp_to_proto(timestamp: &Timestamp) -> crate::protos::op_store::Timestamp {
let mut proto = crate::protos::op_store::Timestamp::new();
proto.millis_since_epoch = timestamp.timestamp.0;
proto.tz_offset = timestamp.tz_offset;
proto
crate::protos::op_store::Timestamp {
millis_since_epoch: timestamp.timestamp.0,
tz_offset: timestamp.tz_offset,
}
}
fn timestamp_from_proto(proto: &crate::protos::op_store::Timestamp) -> Timestamp {
fn timestamp_from_proto(proto: crate::protos::op_store::Timestamp) -> Timestamp {
Timestamp {
timestamp: MillisSinceEpoch(proto.millis_since_epoch),
tz_offset: proto.tz_offset,
@ -127,50 +126,47 @@ fn timestamp_from_proto(proto: &crate::protos::op_store::Timestamp) -> Timestamp
fn operation_metadata_to_proto(
metadata: &OperationMetadata,
) -> crate::protos::op_store::OperationMetadata {
let mut proto = crate::protos::op_store::OperationMetadata::new();
proto.start_time = MessageField::some(timestamp_to_proto(&metadata.start_time));
proto.end_time = MessageField::some(timestamp_to_proto(&metadata.end_time));
proto.description = metadata.description.clone();
proto.hostname = metadata.hostname.clone();
proto.username = metadata.username.clone();
proto.tags = metadata.tags.clone();
proto
crate::protos::op_store::OperationMetadata {
start_time: Some(timestamp_to_proto(&metadata.start_time)),
end_time: Some(timestamp_to_proto(&metadata.end_time)),
description: metadata.description.clone(),
hostname: metadata.hostname.clone(),
username: metadata.username.clone(),
tags: metadata.tags.clone(),
}
}
fn operation_metadata_from_proto(
proto: &crate::protos::op_store::OperationMetadata,
proto: crate::protos::op_store::OperationMetadata,
) -> OperationMetadata {
let start_time = timestamp_from_proto(&proto.start_time);
let end_time = timestamp_from_proto(&proto.end_time);
let description = proto.description.to_owned();
let hostname = proto.hostname.to_owned();
let username = proto.username.to_owned();
let tags = proto.tags.clone();
let start_time = timestamp_from_proto(proto.start_time.unwrap_or_default());
let end_time = timestamp_from_proto(proto.end_time.unwrap_or_default());
OperationMetadata {
start_time,
end_time,
description,
hostname,
username,
tags,
description: proto.description,
hostname: proto.hostname,
username: proto.username,
tags: proto.tags,
}
}
fn operation_to_proto(operation: &Operation) -> crate::protos::op_store::Operation {
let mut proto = crate::protos::op_store::Operation::new();
proto.view_id = operation.view_id.as_bytes().to_vec();
let mut proto = crate::protos::op_store::Operation {
view_id: operation.view_id.as_bytes().to_vec(),
metadata: Some(operation_metadata_to_proto(&operation.metadata)),
..Default::default()
};
for parent in &operation.parents {
proto.parents.push(parent.to_bytes());
}
proto.metadata = MessageField::some(operation_metadata_to_proto(&operation.metadata));
proto
}
fn operation_from_proto(proto: &crate::protos::op_store::Operation) -> Operation {
let operation_id_from_proto = |parent: &Vec<u8>| OperationId::new(parent.clone());
let parents = proto.parents.iter().map(operation_id_from_proto).collect();
let view_id = ViewId::new(proto.view_id.clone());
let metadata = operation_metadata_from_proto(&proto.metadata);
fn operation_from_proto(proto: crate::protos::op_store::Operation) -> Operation {
let parents = proto.parents.into_iter().map(OperationId::new).collect();
let view_id = ViewId::new(proto.view_id);
let metadata = operation_metadata_from_proto(proto.metadata.unwrap_or_default());
Operation {
view_id,
parents,
@ -179,7 +175,7 @@ fn operation_from_proto(proto: &crate::protos::op_store::Operation) -> Operation
}
fn view_to_proto(view: &View) -> crate::protos::op_store::View {
let mut proto = crate::protos::op_store::View::new();
let mut proto = crate::protos::op_store::View::default();
for (workspace_id, commit_id) in &view.wc_commit_ids {
proto
.wc_commit_ids
@ -193,32 +189,38 @@ fn view_to_proto(view: &View) -> crate::protos::op_store::View {
}
for (name, target) in &view.branches {
let mut branch_proto = crate::protos::op_store::Branch::new();
let mut branch_proto = crate::protos::op_store::Branch {
name: name.clone(),
..Default::default()
};
branch_proto.name = name.clone();
if let Some(local_target) = &target.local_target {
branch_proto.local_target = MessageField::some(ref_target_to_proto(local_target));
branch_proto.local_target = Some(ref_target_to_proto(local_target));
}
for (remote_name, target) in &target.remote_targets {
let mut remote_branch_proto = crate::protos::op_store::RemoteBranch::new();
remote_branch_proto.remote_name = remote_name.clone();
remote_branch_proto.target = MessageField::some(ref_target_to_proto(target));
branch_proto.remote_branches.push(remote_branch_proto);
branch_proto
.remote_branches
.push(crate::protos::op_store::RemoteBranch {
remote_name: remote_name.clone(),
target: Some(ref_target_to_proto(target)),
});
}
proto.branches.push(branch_proto);
}
for (name, target) in &view.tags {
let mut tag_proto = crate::protos::op_store::Tag::new();
tag_proto.name = name.clone();
tag_proto.target = MessageField::some(ref_target_to_proto(target));
proto.tags.push(tag_proto);
proto.tags.push(crate::protos::op_store::Tag {
name: name.clone(),
target: Some(ref_target_to_proto(target)),
});
}
for (git_ref_name, target) in &view.git_refs {
let mut git_ref_proto = crate::protos::op_store::GitRef::new();
git_ref_proto.name = git_ref_name.clone();
git_ref_proto.target = MessageField::some(ref_target_to_proto(target));
proto.git_refs.push(git_ref_proto);
proto.git_refs.push(crate::protos::op_store::GitRef {
name: git_ref_name.clone(),
target: Some(ref_target_to_proto(target)),
..Default::default()
});
}
if let Some(git_head) = &view.git_head {
@ -228,41 +230,34 @@ fn view_to_proto(view: &View) -> crate::protos::op_store::View {
proto
}
fn view_from_proto(proto: &crate::protos::op_store::View) -> View {
fn view_from_proto(proto: crate::protos::op_store::View) -> View {
let mut view = View::default();
// For compatibility with old repos before we had support for multiple working
// copies
#[allow(deprecated)]
if !proto.wc_commit_id.is_empty() {
view.wc_commit_ids.insert(
WorkspaceId::default(),
CommitId::new(proto.wc_commit_id.clone()),
);
view.wc_commit_ids
.insert(WorkspaceId::default(), CommitId::new(proto.wc_commit_id));
}
for (workspace_id, commit_id) in &proto.wc_commit_ids {
view.wc_commit_ids.insert(
WorkspaceId::new(workspace_id.clone()),
CommitId::new(commit_id.clone()),
);
for (workspace_id, commit_id) in proto.wc_commit_ids {
view.wc_commit_ids
.insert(WorkspaceId::new(workspace_id), CommitId::new(commit_id));
}
for head_id_bytes in &proto.head_ids {
view.head_ids.insert(CommitId::from_bytes(head_id_bytes));
for head_id_bytes in proto.head_ids {
view.head_ids.insert(CommitId::new(head_id_bytes));
}
for head_id_bytes in &proto.public_head_ids {
view.public_head_ids
.insert(CommitId::from_bytes(head_id_bytes));
for head_id_bytes in proto.public_head_ids {
view.public_head_ids.insert(CommitId::new(head_id_bytes));
}
for branch_proto in &proto.branches {
let local_target = branch_proto
.local_target
.as_ref()
.map(ref_target_from_proto);
for branch_proto in proto.branches {
let local_target = branch_proto.local_target.map(ref_target_from_proto);
let mut remote_targets = BTreeMap::new();
for remote_branch in &branch_proto.remote_branches {
for remote_branch in branch_proto.remote_branches {
remote_targets.insert(
remote_branch.remote_name.clone(),
ref_target_from_proto(&remote_branch.target),
remote_branch.remote_name,
ref_target_from_proto(remote_branch.target.unwrap_or_default()),
);
}
@ -275,69 +270,69 @@ fn view_from_proto(proto: &crate::protos::op_store::View) -> View {
);
}
for tag_proto in &proto.tags {
for tag_proto in proto.tags {
view.tags.insert(
tag_proto.name.clone(),
ref_target_from_proto(&tag_proto.target),
tag_proto.name,
ref_target_from_proto(tag_proto.target.unwrap_or_default()),
);
}
for git_ref in &proto.git_refs {
if let Some(target) = git_ref.target.as_ref() {
for git_ref in proto.git_refs {
if let Some(target) = git_ref.target {
view.git_refs
.insert(git_ref.name.clone(), ref_target_from_proto(target));
.insert(git_ref.name, ref_target_from_proto(target));
} else {
// Legacy format
view.git_refs.insert(
git_ref.name.clone(),
RefTarget::Normal(CommitId::new(git_ref.commit_id.clone())),
git_ref.name,
RefTarget::Normal(CommitId::new(git_ref.commit_id)),
);
}
}
if !proto.git_head.is_empty() {
view.git_head = Some(CommitId::new(proto.git_head.clone()));
view.git_head = Some(CommitId::new(proto.git_head));
}
view
}
fn ref_target_to_proto(value: &RefTarget) -> crate::protos::op_store::RefTarget {
let mut proto = crate::protos::op_store::RefTarget::new();
let mut proto = crate::protos::op_store::RefTarget::default();
match value {
RefTarget::Normal(id) => {
proto.set_commit_id(id.to_bytes());
proto.value = Some(crate::protos::op_store::ref_target::Value::CommitId(
id.to_bytes(),
));
}
RefTarget::Conflict { removes, adds } => {
let mut ref_conflict_proto = crate::protos::op_store::RefConflict::new();
let mut ref_conflict_proto = crate::protos::op_store::RefConflict::default();
for id in removes {
ref_conflict_proto.removes.push(id.to_bytes());
}
for id in adds {
ref_conflict_proto.adds.push(id.to_bytes());
}
proto.set_conflict(ref_conflict_proto);
proto.value = Some(crate::protos::op_store::ref_target::Value::Conflict(
ref_conflict_proto,
));
}
}
proto
}
fn ref_target_from_proto(proto: &crate::protos::op_store::RefTarget) -> RefTarget {
match proto.value.as_ref().unwrap() {
fn ref_target_from_proto(proto: crate::protos::op_store::RefTarget) -> RefTarget {
match proto.value.unwrap() {
crate::protos::op_store::ref_target::Value::CommitId(id) => {
RefTarget::Normal(CommitId::from_bytes(id))
RefTarget::Normal(CommitId::new(id))
}
crate::protos::op_store::ref_target::Value::Conflict(conflict) => {
let removes = conflict
.removes
.iter()
.map(|id_bytes| CommitId::from_bytes(id_bytes))
.collect_vec();
let adds = conflict
.adds
.iter()
.map(|id_bytes| CommitId::from_bytes(id_bytes))
.into_iter()
.map(CommitId::new)
.collect_vec();
let adds = conflict.adds.into_iter().map(CommitId::new).collect_vec();
RefTarget::Conflict { removes, adds }
}
}

View file

@ -1,15 +1,9 @@
// Copyright 2020 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
pub mod op_store {
include!("op_store.rs");
}
pub mod store {
include!("store.rs");
}
pub mod working_copy {
include!("working_copy.rs");
}

132
lib/src/protos/op_store.rs Normal file
View file

@ -0,0 +1,132 @@
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RefConflict {
#[prost(bytes = "vec", repeated, tag = "1")]
pub removes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
#[prost(bytes = "vec", repeated, tag = "2")]
pub adds: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RefTarget {
#[prost(oneof = "ref_target::Value", tags = "1, 2")]
pub value: ::core::option::Option<ref_target::Value>,
}
/// Nested message and enum types in `RefTarget`.
pub mod ref_target {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Value {
#[prost(bytes, tag = "1")]
CommitId(::prost::alloc::vec::Vec<u8>),
#[prost(message, tag = "2")]
Conflict(super::RefConflict),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RemoteBranch {
#[prost(string, tag = "1")]
pub remote_name: ::prost::alloc::string::String,
#[prost(message, optional, tag = "2")]
pub target: ::core::option::Option<RefTarget>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Branch {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
/// Unset if the branch has been deleted locally.
#[prost(message, optional, tag = "2")]
pub local_target: ::core::option::Option<RefTarget>,
/// TODO: How would we support renaming remotes while having undo work? If
/// the remote name is stored in config, it's going to become a mess if the
/// remote is renamed but the configs are left unchanged. Should each remote
/// be identified (here and in configs) by a UUID?
#[prost(message, repeated, tag = "3")]
pub remote_branches: ::prost::alloc::vec::Vec<RemoteBranch>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GitRef {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
/// This field is just for historical reasons (before we had the RefTarget
/// type). New GitRefs have (only) the target field.
/// TODO: Delete support for the old format.
#[prost(bytes = "vec", tag = "2")]
pub commit_id: ::prost::alloc::vec::Vec<u8>,
#[prost(message, optional, tag = "3")]
pub target: ::core::option::Option<RefTarget>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Tag {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
#[prost(message, optional, tag = "2")]
pub target: ::core::option::Option<RefTarget>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct View {
#[prost(bytes = "vec", repeated, tag = "1")]
pub head_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
#[prost(bytes = "vec", repeated, tag = "4")]
pub public_head_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
#[deprecated]
#[prost(bytes = "vec", tag = "2")]
pub wc_commit_id: ::prost::alloc::vec::Vec<u8>,
#[prost(map = "string, bytes", tag = "8")]
pub wc_commit_ids: ::std::collections::HashMap<
::prost::alloc::string::String,
::prost::alloc::vec::Vec<u8>,
>,
#[prost(message, repeated, tag = "5")]
pub branches: ::prost::alloc::vec::Vec<Branch>,
#[prost(message, repeated, tag = "6")]
pub tags: ::prost::alloc::vec::Vec<Tag>,
/// Only a subset of the refs. For example, does not include refs/notes/.
#[prost(message, repeated, tag = "3")]
pub git_refs: ::prost::alloc::vec::Vec<GitRef>,
#[prost(bytes = "vec", tag = "7")]
pub git_head: ::prost::alloc::vec::Vec<u8>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Operation {
#[prost(bytes = "vec", tag = "1")]
pub view_id: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", repeated, tag = "2")]
pub parents: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
#[prost(message, optional, tag = "3")]
pub metadata: ::core::option::Option<OperationMetadata>,
}
/// TODO: Share with store.proto? Do we even need the timezone here?
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Timestamp {
#[prost(int64, tag = "1")]
pub millis_since_epoch: i64,
#[prost(int32, tag = "2")]
pub tz_offset: i32,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct OperationMetadata {
#[prost(message, optional, tag = "1")]
pub start_time: ::core::option::Option<Timestamp>,
#[prost(message, optional, tag = "2")]
pub end_time: ::core::option::Option<Timestamp>,
#[prost(string, tag = "3")]
pub description: ::prost::alloc::string::String,
#[prost(string, tag = "4")]
pub hostname: ::prost::alloc::string::String,
#[prost(string, tag = "5")]
pub username: ::prost::alloc::string::String,
#[prost(map = "string, string", tag = "6")]
pub tags: ::std::collections::HashMap<
::prost::alloc::string::String,
::prost::alloc::string::String,
>,
}

108
lib/src/protos/store.rs Normal file
View file

@ -0,0 +1,108 @@
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TreeValue {
#[prost(oneof = "tree_value::Value", tags = "2, 3, 4, 5")]
pub value: ::core::option::Option<tree_value::Value>,
}
/// Nested message and enum types in `TreeValue`.
pub mod tree_value {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct File {
#[prost(bytes = "vec", tag = "1")]
pub id: ::prost::alloc::vec::Vec<u8>,
#[prost(bool, tag = "2")]
pub executable: bool,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Value {
#[prost(message, tag = "2")]
File(File),
#[prost(bytes, tag = "3")]
SymlinkId(::prost::alloc::vec::Vec<u8>),
#[prost(bytes, tag = "4")]
TreeId(::prost::alloc::vec::Vec<u8>),
#[prost(bytes, tag = "5")]
ConflictId(::prost::alloc::vec::Vec<u8>),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Tree {
#[prost(message, repeated, tag = "1")]
pub entries: ::prost::alloc::vec::Vec<tree::Entry>,
}
/// Nested message and enum types in `Tree`.
pub mod tree {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Entry {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
#[prost(message, optional, tag = "2")]
pub value: ::core::option::Option<super::TreeValue>,
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Commit {
#[prost(bytes = "vec", repeated, tag = "1")]
pub parents: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
#[prost(bytes = "vec", repeated, tag = "2")]
pub predecessors: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
#[prost(bytes = "vec", tag = "3")]
pub root_tree: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "4")]
pub change_id: ::prost::alloc::vec::Vec<u8>,
#[prost(string, tag = "5")]
pub description: ::prost::alloc::string::String,
#[prost(message, optional, tag = "6")]
pub author: ::core::option::Option<commit::Signature>,
#[prost(message, optional, tag = "7")]
pub committer: ::core::option::Option<commit::Signature>,
#[deprecated]
#[prost(bool, tag = "8")]
pub is_open: bool,
#[deprecated]
#[prost(bool, tag = "9")]
pub is_pruned: bool,
}
/// Nested message and enum types in `Commit`.
pub mod commit {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Timestamp {
#[prost(int64, tag = "1")]
pub millis_since_epoch: i64,
#[prost(int32, tag = "2")]
pub tz_offset: i32,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Signature {
#[prost(string, tag = "1")]
pub name: ::prost::alloc::string::String,
#[prost(string, tag = "2")]
pub email: ::prost::alloc::string::String,
#[prost(message, optional, tag = "3")]
pub timestamp: ::core::option::Option<Timestamp>,
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Conflict {
#[prost(message, repeated, tag = "1")]
pub removes: ::prost::alloc::vec::Vec<conflict::Part>,
#[prost(message, repeated, tag = "2")]
pub adds: ::prost::alloc::vec::Vec<conflict::Part>,
}
/// Nested message and enum types in `Conflict`.
pub mod conflict {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Part {
#[prost(message, optional, tag = "1")]
pub content: ::core::option::Option<super::TreeValue>,
}
}

View file

@ -0,0 +1,85 @@
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FileState {
#[prost(int64, tag = "1")]
pub mtime_millis_since_epoch: i64,
#[prost(uint64, tag = "2")]
pub size: u64,
#[prost(enumeration = "FileType", tag = "3")]
pub file_type: i32,
/// Set only if file_type is Conflict
#[prost(bytes = "vec", tag = "4")]
pub conflict_id: ::prost::alloc::vec::Vec<u8>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SparsePatterns {
#[prost(string, repeated, tag = "1")]
pub prefixes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TreeState {
#[prost(bytes = "vec", tag = "1")]
pub tree_id: ::prost::alloc::vec::Vec<u8>,
#[prost(map = "string, message", tag = "2")]
pub file_states: ::std::collections::HashMap<
::prost::alloc::string::String,
FileState,
>,
#[prost(message, optional, tag = "3")]
pub sparse_patterns: ::core::option::Option<SparsePatterns>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Checkout {
/// The operation at which the working copy was updated.
#[prost(bytes = "vec", tag = "2")]
pub operation_id: ::prost::alloc::vec::Vec<u8>,
/// An identifier for this workspace. It is used for looking up the current
/// checkout in the repo view. Currently a human-readable name.
/// TODO: Is it better to make this a UUID and a have map that to a name in
/// config? That way users can rename a workspace.
#[prost(string, tag = "3")]
pub workspace_id: ::prost::alloc::string::String,
/// The checked-out commit, which can be viewed as a cache of the working-copy
/// commit ID recorded in `operation_id`'s operation. No longer used.
/// TODO: Delete this mid 2022 or so
#[prost(bytes = "vec", tag = "1")]
pub commit_id: ::prost::alloc::vec::Vec<u8>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum FileType {
Normal = 0,
Symlink = 1,
Executable = 2,
Conflict = 3,
GitSubmodule = 4,
}
impl FileType {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
FileType::Normal => "Normal",
FileType::Symlink => "Symlink",
FileType::Executable => "Executable",
FileType::Conflict => "Conflict",
FileType::GitSubmodule => "GitSubmodule",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"Normal" => Some(Self::Normal),
"Symlink" => Some(Self::Symlink),
"Executable" => Some(Self::Executable),
"Conflict" => Some(Self::Conflict),
"GitSubmodule" => Some(Self::GitSubmodule),
_ => None,
}
}
}

View file

@ -39,10 +39,14 @@ impl RepoPathComponent {
impl From<&str> for RepoPathComponent {
fn from(value: &str) -> Self {
RepoPathComponent::from(value.to_owned())
}
}
impl From<String> for RepoPathComponent {
fn from(value: String) -> Self {
assert!(!value.contains('/'));
RepoPathComponent {
value: value.to_owned(),
}
RepoPathComponent { value }
}
}

View file

@ -27,7 +27,7 @@ use std::sync::Arc;
use std::time::UNIX_EPOCH;
use once_cell::unsync::OnceCell;
use protobuf::{EnumOrUnknown, Message, MessageField};
use prost::Message;
use tempfile::NamedTempFile;
use thiserror::Error;
@ -125,13 +125,13 @@ pub struct TreeState {
own_mtime: MillisSinceEpoch,
}
fn file_state_from_proto(proto: &crate::protos::working_copy::FileState) -> FileState {
let file_type = match proto.file_type.enum_value_or_default() {
fn file_state_from_proto(proto: crate::protos::working_copy::FileState) -> FileState {
let file_type = match proto.file_type() {
crate::protos::working_copy::FileType::Normal => FileType::Normal { executable: false },
crate::protos::working_copy::FileType::Executable => FileType::Normal { executable: true },
crate::protos::working_copy::FileType::Symlink => FileType::Symlink,
crate::protos::working_copy::FileType::Conflict => {
let id = ConflictId::new(proto.conflict_id.to_vec());
let id = ConflictId::new(proto.conflict_id);
FileType::Conflict { id }
}
crate::protos::working_copy::FileType::GitSubmodule => FileType::GitSubmodule,
@ -144,7 +144,7 @@ fn file_state_from_proto(proto: &crate::protos::working_copy::FileState) -> File
}
fn file_state_to_proto(file_state: &FileState) -> crate::protos::working_copy::FileState {
let mut proto = crate::protos::working_copy::FileState::new();
let mut proto = crate::protos::working_copy::FileState::default();
let file_type = match &file_state.file_type {
FileType::Normal { executable: false } => crate::protos::working_copy::FileType::Normal,
FileType::Normal { executable: true } => crate::protos::working_copy::FileType::Executable,
@ -155,7 +155,7 @@ fn file_state_to_proto(file_state: &FileState) -> crate::protos::working_copy::F
}
FileType::GitSubmodule => crate::protos::working_copy::FileType::GitSubmodule,
};
proto.file_type = EnumOrUnknown::new(file_type);
proto.file_type = file_type as i32;
proto.mtime_millis_since_epoch = file_state.mtime.0;
proto.size = file_state.size;
proto
@ -167,7 +167,7 @@ fn file_states_from_proto(
let mut file_states = BTreeMap::new();
for (path_str, proto_file_state) in &proto.file_states {
let path = RepoPath::from_internal_string(path_str.as_str());
file_states.insert(path, file_state_from_proto(proto_file_state));
file_states.insert(path, file_state_from_proto(proto_file_state.clone()));
}
file_states
}
@ -401,32 +401,38 @@ impl TreeState {
fn read(&mut self, mut file: File) {
self.update_own_mtime();
let proto: crate::protos::working_copy::TreeState =
Message::parse_from_reader(&mut file).unwrap();
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
let proto = crate::protos::working_copy::TreeState::decode(&*buf).unwrap();
self.tree_id = TreeId::new(proto.tree_id.clone());
self.file_states = file_states_from_proto(&proto);
self.sparse_patterns = sparse_patterns_from_proto(&proto);
}
fn save(&mut self) {
let mut proto = crate::protos::working_copy::TreeState::new();
proto.tree_id = self.tree_id.to_bytes();
let mut proto = crate::protos::working_copy::TreeState {
tree_id: self.tree_id.to_bytes(),
..Default::default()
};
for (file, file_state) in &self.file_states {
proto.file_states.insert(
file.to_internal_file_string(),
file_state_to_proto(file_state),
);
}
let mut sparse_patterns = crate::protos::working_copy::SparsePatterns::new();
let mut sparse_patterns = crate::protos::working_copy::SparsePatterns::default();
for path in &self.sparse_patterns {
sparse_patterns
.prefixes
.push(path.to_internal_file_string());
}
proto.sparse_patterns = MessageField::some(sparse_patterns);
proto.sparse_patterns = Some(sparse_patterns);
let mut temp_file = NamedTempFile::new_in(&self.state_path).unwrap();
proto.write_to_writer(temp_file.as_file_mut()).unwrap();
temp_file
.as_file_mut()
.write_all(&proto.encode_to_vec())
.unwrap();
// update own write time while we before we rename it, so we know
// there is no unknown data in it
self.update_own_mtime();
@ -1012,15 +1018,17 @@ impl WorkingCopy {
operation_id: OperationId,
workspace_id: WorkspaceId,
) -> WorkingCopy {
let mut proto = crate::protos::working_copy::Checkout::new();
proto.operation_id = operation_id.to_bytes();
proto.workspace_id = workspace_id.as_str().to_string();
let proto = crate::protos::working_copy::Checkout {
operation_id: operation_id.to_bytes(),
workspace_id: workspace_id.as_str().to_string(),
..Default::default()
};
let mut file = OpenOptions::new()
.create_new(true)
.write(true)
.open(state_path.join("checkout"))
.unwrap();
proto.write_to_writer(&mut file).unwrap();
file.write_all(&proto.encode_to_vec()).unwrap();
WorkingCopy {
store,
working_copy_path,
@ -1050,7 +1058,10 @@ impl WorkingCopy {
fn write_proto(&self, proto: crate::protos::working_copy::Checkout) {
let mut temp_file = NamedTempFile::new_in(&self.state_path).unwrap();
proto.write_to_writer(temp_file.as_file_mut()).unwrap();
temp_file
.as_file_mut()
.write_all(&proto.encode_to_vec())
.unwrap();
// TODO: Retry if persisting fails (it will on Windows if the file happened to
// be open for read).
temp_file.persist(self.state_path.join("checkout")).unwrap();
@ -1058,9 +1069,8 @@ impl WorkingCopy {
fn checkout_state(&self) -> &CheckoutState {
self.checkout_state.get_or_init(|| {
let mut file = File::open(self.state_path.join("checkout")).unwrap();
let proto: crate::protos::working_copy::Checkout =
Message::parse_from_reader(&mut file).unwrap();
let buf = fs::read(self.state_path.join("checkout")).unwrap();
let proto = crate::protos::working_copy::Checkout::decode(&*buf).unwrap();
CheckoutState {
operation_id: OperationId::new(proto.operation_id),
workspace_id: if proto.workspace_id.is_empty() {
@ -1115,10 +1125,11 @@ impl WorkingCopy {
}
fn save(&mut self) {
let mut proto = crate::protos::working_copy::Checkout::new();
proto.operation_id = self.operation_id().to_bytes();
proto.workspace_id = self.workspace_id().as_str().to_string();
self.write_proto(proto);
self.write_proto(crate::protos::working_copy::Checkout {
operation_id: self.operation_id().to_bytes(),
workspace_id: self.workspace_id().as_str().to_string(),
..Default::default()
});
}
pub fn start_mutation(&mut self) -> LockedWorkingCopy {