local_backend: switch from Protobuf to Thrift

This migrates the native backend from Protobuf to Thrift since
Google's Protobuf team does let us import jj into Google's monorepo if
it uses a third-party Protobuf library.

Since the native backend is not supported, I didn't write any
migration code for it.

We can't remove `lib/src/protos/store.proto` yet, because it's also
used by the Git backend (only the `predecessors` and `change_id`
fields).
This commit is contained in:
Martin von Zweigbergk 2022-11-03 22:23:24 -07:00 committed by Martin von Zweigbergk
parent 2ae2010007
commit 5b10c9aa0a
5 changed files with 971 additions and 118 deletions

View file

@ -26,6 +26,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
automatically upgraded the first time you run a command in an existing repo.
The operation IDs will change in that process.
* The storage format for the native backend has changed. Unlike the operation
log, it will *not* be automatically upgraded. We consider the native backend
a proof-of-concept that users should not use.
### New features
* The new `jj git remote rename` command allows git remotes to be renamed

View file

@ -31,6 +31,7 @@ pub mod gitignore;
pub mod index;
pub mod index_store;
pub mod local_backend;
mod local_backend_model;
pub mod lock;
pub mod matchers;
pub mod nightly_shims;

View file

@ -19,8 +19,8 @@ use std::io::{ErrorKind, Read, Write};
use std::path::{Path, PathBuf};
use blake2::{Blake2b512, Digest};
use protobuf::{Message, MessageField};
use tempfile::{NamedTempFile, PersistError};
use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol, TSerializable};
use crate::backend::{
make_root_commit, Backend, BackendError, BackendResult, ChangeId, Commit, CommitId, Conflict,
@ -29,6 +29,7 @@ use crate::backend::{
};
use crate::content_hash::ContentHash;
use crate::file_util::persist_content_addressed_temp_file;
use crate::local_backend_model;
use crate::repo_path::{RepoPath, RepoPathComponent};
impl From<std::io::Error> for BackendError {
@ -43,8 +44,8 @@ impl From<PersistError> for BackendError {
}
}
impl From<protobuf::Error> for BackendError {
fn from(err: protobuf::Error) -> Self {
impl From<thrift::Error> for BackendError {
fn from(err: thrift::Error) -> Self {
BackendError::Other(err.to_string())
}
}
@ -184,19 +185,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 proto: crate::protos::store::Tree = Message::parse_from_reader(&mut file)?;
Ok(tree_from_proto(&proto))
let thrift_tree = read_thrift(&mut file).unwrap();
Ok(tree_from_thrift(&thrift_tree))
}
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())?;
let thrift_tree = tree_to_thrift(tree);
write_thrift(&thrift_tree, &mut temp_file.as_file())?;
let id = TreeId::new(hash(tree).to_vec());
persist_content_addressed_temp_file(temp_file, self.tree_path(&id))?;
Ok(id)
}
@ -204,19 +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 proto: crate::protos::store::Conflict = Message::parse_from_reader(&mut file)?;
Ok(conflict_from_proto(&proto))
let thrift_conflict = read_thrift(&mut file)?;
Ok(conflict_from_thrift(&thrift_conflict))
}
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())?;
let thrift_conflict = conflict_to_thrift(conflict);
write_thrift(&thrift_conflict, &mut temp_file.as_file())?;
let id = ConflictId::new(hash(conflict).to_vec());
persist_content_addressed_temp_file(temp_file, self.conflict_path(&id))?;
Ok(id)
}
@ -228,142 +225,150 @@ impl Backend for LocalBackend {
let path = self.commit_path(id);
let mut file = File::open(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 thrift_commit = read_thrift(&mut file).unwrap();
Ok(commit_from_thrift(&thrift_commit))
}
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())?;
let thrift_commit = commit_to_thrift(commit);
write_thrift(&thrift_commit, &mut temp_file.as_file())?;
let id = CommitId::new(hash(commit).to_vec());
persist_content_addressed_temp_file(temp_file, self.commit_path(&id))?;
Ok(id)
}
}
pub fn commit_to_proto(commit: &Commit) -> crate::protos::store::Commit {
let mut proto = crate::protos::store::Commit::new();
for parent in &commit.parents {
proto.parents.push(parent.to_bytes());
}
for predecessor in &commit.predecessors {
proto.predecessors.push(predecessor.to_bytes());
}
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
fn read_thrift<T: TSerializable>(input: &mut impl Read) -> BackendResult<T> {
let mut protocol = TCompactInputProtocol::new(input);
Ok(TSerializable::read_from_in_protocol(&mut protocol).unwrap())
}
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
fn write_thrift<T: TSerializable>(thrift_object: &T, output: &mut impl Write) -> BackendResult<()> {
let mut protocol = TCompactOutputProtocol::new(output);
thrift_object.write_to_out_protocol(&mut protocol)?;
Ok(())
}
fn commit_to_thrift(commit: &Commit) -> local_backend_model::Commit {
let mut parents = vec![];
for parent in &commit.parents {
parents.push(parent.to_bytes());
}
let mut predecessors = vec![];
for predecessor in &commit.predecessors {
predecessors.push(predecessor.to_bytes());
}
let root_tree = commit.root_tree.to_bytes();
let change_id = commit.change_id.to_bytes();
let description = commit.description.clone();
let author = signature_to_thrift(&commit.author);
let committer = signature_to_thrift(&commit.committer);
local_backend_model::Commit::new(
parents,
predecessors,
root_tree,
change_id,
description,
author,
committer,
)
}
fn commit_from_thrift(thrift_commit: &local_backend_model::Commit) -> Commit {
let commit_id_from_thrift = |parent: &Vec<u8>| CommitId::new(parent.clone());
let parents = thrift_commit
.parents
.iter()
.map(commit_id_from_thrift)
.collect();
let predecessors = thrift_commit
.predecessors
.iter()
.map(commit_id_from_proto)
.map(commit_id_from_thrift)
.collect();
let root_tree = TreeId::new(proto.root_tree.to_vec());
let change_id = ChangeId::new(proto.change_id.to_vec());
let root_tree = TreeId::new(thrift_commit.root_tree.to_vec());
let change_id = ChangeId::new(thrift_commit.change_id.to_vec());
Commit {
parents,
predecessors,
root_tree,
change_id,
description: proto.description.clone(),
author: signature_from_proto(&proto.author),
committer: signature_from_proto(&proto.committer),
description: thrift_commit.description.clone(),
author: signature_from_thrift(&thrift_commit.author),
committer: signature_from_thrift(&thrift_commit.committer),
}
}
fn tree_to_proto(tree: &Tree) -> crate::protos::store::Tree {
let mut proto = crate::protos::store::Tree::new();
fn tree_to_thrift(tree: &Tree) -> local_backend_model::Tree {
let mut entries = vec![];
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);
let name = entry.name().string();
let value = tree_value_to_thrift(entry.value());
let thrift_entry = local_backend_model::TreeEntry::new(name, value);
entries.push(thrift_entry);
}
proto
local_backend_model::Tree::new(entries)
}
fn tree_from_proto(proto: &crate::protos::store::Tree) -> Tree {
fn tree_from_thrift(thrift_tree: &local_backend_model::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 thrift_tree_entry in &thrift_tree.entries {
let value = tree_value_from_thrift(&thrift_tree_entry.value);
tree.set(
RepoPathComponent::from(thrift_tree_entry.name.as_str()),
value,
);
}
tree
}
fn tree_value_to_proto(value: &TreeValue) -> crate::protos::store::TreeValue {
let mut proto = crate::protos::store::TreeValue::new();
fn tree_value_to_thrift(value: &TreeValue) -> local_backend_model::TreeValue {
match value {
TreeValue::Normal { id, executable } => {
let mut file = crate::protos::store::tree_value::NormalFile::new();
file.id = id.to_bytes();
file.executable = *executable;
proto.set_normal_file(file);
}
TreeValue::Symlink(id) => {
proto.set_symlink_id(id.to_bytes());
let file = local_backend_model::NormalFile::new(id.to_bytes(), *executable);
local_backend_model::TreeValue::NormalFile(file)
}
TreeValue::Symlink(id) => local_backend_model::TreeValue::SymlinkId(id.to_bytes()),
TreeValue::GitSubmodule(_id) => {
panic!("cannot store git submodules");
}
TreeValue::Tree(id) => {
proto.set_tree_id(id.to_bytes());
}
TreeValue::Conflict(id) => {
proto.set_conflict_id(id.to_bytes());
}
};
proto
TreeValue::Tree(id) => local_backend_model::TreeValue::TreeId(id.to_bytes()),
TreeValue::Conflict(id) => local_backend_model::TreeValue::ConflictId(id.to_bytes()),
}
}
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()))
}
crate::protos::store::tree_value::Value::NormalFile(
crate::protos::store::tree_value::NormalFile { id, executable, .. },
) => TreeValue::Normal {
id: FileId::new(id.clone()),
executable: *executable,
fn tree_value_from_thrift(thrift_tree_value: &local_backend_model::TreeValue) -> TreeValue {
match thrift_tree_value {
local_backend_model::TreeValue::NormalFile(file) => TreeValue::Normal {
id: FileId::from_bytes(&file.id),
executable: file.executable,
},
crate::protos::store::tree_value::Value::SymlinkId(id) => {
TreeValue::Symlink(SymlinkId::new(id.clone()))
local_backend_model::TreeValue::SymlinkId(id) => {
TreeValue::Symlink(SymlinkId::from_bytes(id))
}
crate::protos::store::tree_value::Value::ConflictId(id) => {
TreeValue::Conflict(ConflictId::new(id.clone()))
local_backend_model::TreeValue::TreeId(id) => TreeValue::Tree(TreeId::from_bytes(id)),
local_backend_model::TreeValue::ConflictId(id) => {
TreeValue::Conflict(ConflictId::from_bytes(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
fn signature_to_thrift(signature: &Signature) -> local_backend_model::Signature {
let timestamp = local_backend_model::Timestamp::new(
signature.timestamp.timestamp.0,
signature.timestamp.tz_offset,
);
local_backend_model::Signature::new(signature.name.clone(), signature.email.clone(), timestamp)
}
fn signature_from_proto(proto: &crate::protos::store::commit::Signature) -> Signature {
let timestamp = &proto.timestamp;
fn signature_from_thrift(thrift: &local_backend_model::Signature) -> Signature {
let timestamp = &thrift.timestamp;
Signature {
name: proto.name.clone(),
email: proto.email.clone(),
name: thrift.name.clone(),
email: thrift.email.clone(),
timestamp: Timestamp {
timestamp: MillisSinceEpoch(timestamp.millis_since_epoch),
tz_offset: timestamp.tz_offset,
@ -371,38 +376,37 @@ 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();
for part in &conflict.adds {
proto.adds.push(conflict_part_to_proto(part));
}
fn conflict_to_thrift(conflict: &Conflict) -> local_backend_model::Conflict {
let mut removes = vec![];
for part in &conflict.removes {
proto.removes.push(conflict_part_to_proto(part));
removes.push(conflict_part_to_thrift(part));
}
proto
let mut adds = vec![];
for part in &conflict.adds {
adds.push(conflict_part_to_thrift(part));
}
local_backend_model::Conflict::new(removes, adds)
}
fn conflict_from_proto(proto: &crate::protos::store::Conflict) -> Conflict {
fn conflict_from_thrift(thrift: &local_backend_model::Conflict) -> Conflict {
let mut conflict = Conflict::default();
for part in &proto.removes {
conflict.removes.push(conflict_part_from_proto(part))
for part in &thrift.removes {
conflict.removes.push(conflict_part_from_thrift(part))
}
for part in &proto.adds {
conflict.adds.push(conflict_part_from_proto(part))
for part in &thrift.adds {
conflict.adds.push(conflict_part_from_thrift(part))
}
conflict
}
fn conflict_part_from_proto(proto: &crate::protos::store::conflict::Part) -> ConflictPart {
fn conflict_part_from_thrift(thrift: &local_backend_model::ConflictPart) -> ConflictPart {
ConflictPart {
value: tree_value_from_proto(proto.content.as_ref().unwrap()),
value: tree_value_from_thrift(&thrift.content),
}
}
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
fn conflict_part_to_thrift(part: &ConflictPart) -> local_backend_model::ConflictPart {
local_backend_model::ConflictPart::new(tree_value_to_thrift(&part.value))
}
fn hash(x: &impl ContentHash) -> digest::Output<Blake2b512> {

View file

@ -0,0 +1,780 @@
// Autogenerated by Thrift Compiler (0.17.0)
// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#![allow(unused_imports)]
#![allow(unused_extern_crates)]
#![allow(clippy::too_many_arguments, clippy::type_complexity, clippy::vec_box)]
#![cfg_attr(rustfmt, rustfmt_skip)]
use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet};
use std::convert::{From, TryFrom};
use std::default::Default;
use std::error::Error;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::rc::Rc;
use thrift::OrderedFloat;
use thrift::{ApplicationError, ApplicationErrorKind, ProtocolError, ProtocolErrorKind, TThriftClient};
use thrift::protocol::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType, TInputProtocol, TOutputProtocol, TSerializable, TSetIdentifier, TStructIdentifier, TType};
use thrift::protocol::field_id;
use thrift::protocol::verify_expected_message_type;
use thrift::protocol::verify_expected_sequence_number;
use thrift::protocol::verify_expected_service_call;
use thrift::protocol::verify_required_field_exists;
//
// NormalFile
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NormalFile {
pub id: Vec<u8>,
pub executable: bool,
}
impl NormalFile {
pub fn new(id: Vec<u8>, executable: bool) -> NormalFile {
NormalFile {
id,
executable,
}
}
}
impl TSerializable for NormalFile {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<NormalFile> {
i_prot.read_struct_begin()?;
let mut f_1: Option<Vec<u8>> = None;
let mut f_2: Option<bool> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let val = i_prot.read_bytes()?;
f_1 = Some(val);
},
2 => {
let val = i_prot.read_bool()?;
f_2 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("NormalFile.id", &f_1)?;
verify_required_field_exists("NormalFile.executable", &f_2)?;
let ret = NormalFile {
id: f_1.expect("auto-generated code should have checked for presence of required fields"),
executable: f_2.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("NormalFile");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("id", TType::String, 1))?;
o_prot.write_bytes(&self.id)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("executable", TType::Bool, 2))?;
o_prot.write_bool(self.executable)?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// TreeValue
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum TreeValue {
NormalFile(NormalFile),
SymlinkId(Vec<u8>),
TreeId(Vec<u8>),
ConflictId(Vec<u8>),
}
impl TSerializable for TreeValue {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<TreeValue> {
let mut ret: Option<TreeValue> = None;
let mut received_field_count = 0;
i_prot.read_struct_begin()?;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let val = NormalFile::read_from_in_protocol(i_prot)?;
if ret.is_none() {
ret = Some(TreeValue::NormalFile(val));
}
received_field_count += 1;
},
2 => {
let val = i_prot.read_bytes()?;
if ret.is_none() {
ret = Some(TreeValue::SymlinkId(val));
}
received_field_count += 1;
},
3 => {
let val = i_prot.read_bytes()?;
if ret.is_none() {
ret = Some(TreeValue::TreeId(val));
}
received_field_count += 1;
},
4 => {
let val = i_prot.read_bytes()?;
if ret.is_none() {
ret = Some(TreeValue::ConflictId(val));
}
received_field_count += 1;
},
_ => {
i_prot.skip(field_ident.field_type)?;
received_field_count += 1;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
if received_field_count == 0 {
Err(
thrift::Error::Protocol(
ProtocolError::new(
ProtocolErrorKind::InvalidData,
"received empty union from remote TreeValue"
)
)
)
} else if received_field_count > 1 {
Err(
thrift::Error::Protocol(
ProtocolError::new(
ProtocolErrorKind::InvalidData,
"received multiple fields for union from remote TreeValue"
)
)
)
} else {
Ok(ret.expect("return value should have been constructed"))
}
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("TreeValue");
o_prot.write_struct_begin(&struct_ident)?;
match *self {
TreeValue::NormalFile(ref f) => {
o_prot.write_field_begin(&TFieldIdentifier::new("normal_file", TType::Struct, 1))?;
f.write_to_out_protocol(o_prot)?;
o_prot.write_field_end()?;
},
TreeValue::SymlinkId(ref f) => {
o_prot.write_field_begin(&TFieldIdentifier::new("symlink_id", TType::String, 2))?;
o_prot.write_bytes(f)?;
o_prot.write_field_end()?;
},
TreeValue::TreeId(ref f) => {
o_prot.write_field_begin(&TFieldIdentifier::new("tree_id", TType::String, 3))?;
o_prot.write_bytes(f)?;
o_prot.write_field_end()?;
},
TreeValue::ConflictId(ref f) => {
o_prot.write_field_begin(&TFieldIdentifier::new("conflict_id", TType::String, 4))?;
o_prot.write_bytes(f)?;
o_prot.write_field_end()?;
},
}
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// TreeEntry
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TreeEntry {
pub name: String,
pub value: TreeValue,
}
impl TreeEntry {
pub fn new(name: String, value: TreeValue) -> TreeEntry {
TreeEntry {
name,
value,
}
}
}
impl TSerializable for TreeEntry {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<TreeEntry> {
i_prot.read_struct_begin()?;
let mut f_1: Option<String> = None;
let mut f_2: Option<TreeValue> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let val = i_prot.read_string()?;
f_1 = Some(val);
},
2 => {
let val = TreeValue::read_from_in_protocol(i_prot)?;
f_2 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("TreeEntry.name", &f_1)?;
verify_required_field_exists("TreeEntry.value", &f_2)?;
let ret = TreeEntry {
name: f_1.expect("auto-generated code should have checked for presence of required fields"),
value: f_2.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("TreeEntry");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("name", TType::String, 1))?;
o_prot.write_string(&self.name)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("value", TType::Struct, 2))?;
self.value.write_to_out_protocol(o_prot)?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// Tree
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Tree {
pub entries: Vec<TreeEntry>,
}
impl Tree {
pub fn new(entries: Vec<TreeEntry>) -> Tree {
Tree {
entries,
}
}
}
impl TSerializable for Tree {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<Tree> {
i_prot.read_struct_begin()?;
let mut f_1: Option<Vec<TreeEntry>> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let list_ident = i_prot.read_list_begin()?;
let mut val: Vec<TreeEntry> = Vec::with_capacity(list_ident.size as usize);
for _ in 0..list_ident.size {
let list_elem_0 = TreeEntry::read_from_in_protocol(i_prot)?;
val.push(list_elem_0);
}
i_prot.read_list_end()?;
f_1 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("Tree.entries", &f_1)?;
let ret = Tree {
entries: f_1.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("Tree");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("entries", TType::List, 1))?;
o_prot.write_list_begin(&TListIdentifier::new(TType::Struct, self.entries.len() as i32))?;
for e in &self.entries {
e.write_to_out_protocol(o_prot)?;
}
o_prot.write_list_end()?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// Timestamp
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Timestamp {
pub millis_since_epoch: i64,
pub tz_offset: i32,
}
impl Timestamp {
pub fn new(millis_since_epoch: i64, tz_offset: i32) -> Timestamp {
Timestamp {
millis_since_epoch,
tz_offset,
}
}
}
impl TSerializable for Timestamp {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<Timestamp> {
i_prot.read_struct_begin()?;
let mut f_1: Option<i64> = None;
let mut f_2: Option<i32> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let val = i_prot.read_i64()?;
f_1 = Some(val);
},
2 => {
let val = i_prot.read_i32()?;
f_2 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("Timestamp.millis_since_epoch", &f_1)?;
verify_required_field_exists("Timestamp.tz_offset", &f_2)?;
let ret = Timestamp {
millis_since_epoch: f_1.expect("auto-generated code should have checked for presence of required fields"),
tz_offset: f_2.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("Timestamp");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("millis_since_epoch", TType::I64, 1))?;
o_prot.write_i64(self.millis_since_epoch)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("tz_offset", TType::I32, 2))?;
o_prot.write_i32(self.tz_offset)?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// Signature
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Signature {
pub name: String,
pub email: String,
pub timestamp: Timestamp,
}
impl Signature {
pub fn new(name: String, email: String, timestamp: Timestamp) -> Signature {
Signature {
name,
email,
timestamp,
}
}
}
impl TSerializable for Signature {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<Signature> {
i_prot.read_struct_begin()?;
let mut f_1: Option<String> = None;
let mut f_2: Option<String> = None;
let mut f_3: Option<Timestamp> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let val = i_prot.read_string()?;
f_1 = Some(val);
},
2 => {
let val = i_prot.read_string()?;
f_2 = Some(val);
},
3 => {
let val = Timestamp::read_from_in_protocol(i_prot)?;
f_3 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("Signature.name", &f_1)?;
verify_required_field_exists("Signature.email", &f_2)?;
verify_required_field_exists("Signature.timestamp", &f_3)?;
let ret = Signature {
name: f_1.expect("auto-generated code should have checked for presence of required fields"),
email: f_2.expect("auto-generated code should have checked for presence of required fields"),
timestamp: f_3.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("Signature");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("name", TType::String, 1))?;
o_prot.write_string(&self.name)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("email", TType::String, 2))?;
o_prot.write_string(&self.email)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("timestamp", TType::Struct, 3))?;
self.timestamp.write_to_out_protocol(o_prot)?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// Commit
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Commit {
pub parents: Vec<Vec<u8>>,
pub predecessors: Vec<Vec<u8>>,
pub root_tree: Vec<u8>,
pub change_id: Vec<u8>,
pub description: String,
pub author: Signature,
pub committer: Signature,
}
impl Commit {
pub fn new(parents: Vec<Vec<u8>>, predecessors: Vec<Vec<u8>>, root_tree: Vec<u8>, change_id: Vec<u8>, description: String, author: Signature, committer: Signature) -> Commit {
Commit {
parents,
predecessors,
root_tree,
change_id,
description,
author,
committer,
}
}
}
impl TSerializable for Commit {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<Commit> {
i_prot.read_struct_begin()?;
let mut f_1: Option<Vec<Vec<u8>>> = None;
let mut f_2: Option<Vec<Vec<u8>>> = None;
let mut f_3: Option<Vec<u8>> = None;
let mut f_4: Option<Vec<u8>> = None;
let mut f_5: Option<String> = None;
let mut f_6: Option<Signature> = None;
let mut f_7: Option<Signature> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let list_ident = i_prot.read_list_begin()?;
let mut val: Vec<Vec<u8>> = Vec::with_capacity(list_ident.size as usize);
for _ in 0..list_ident.size {
let list_elem_1 = i_prot.read_bytes()?;
val.push(list_elem_1);
}
i_prot.read_list_end()?;
f_1 = Some(val);
},
2 => {
let list_ident = i_prot.read_list_begin()?;
let mut val: Vec<Vec<u8>> = Vec::with_capacity(list_ident.size as usize);
for _ in 0..list_ident.size {
let list_elem_2 = i_prot.read_bytes()?;
val.push(list_elem_2);
}
i_prot.read_list_end()?;
f_2 = Some(val);
},
3 => {
let val = i_prot.read_bytes()?;
f_3 = Some(val);
},
4 => {
let val = i_prot.read_bytes()?;
f_4 = Some(val);
},
5 => {
let val = i_prot.read_string()?;
f_5 = Some(val);
},
6 => {
let val = Signature::read_from_in_protocol(i_prot)?;
f_6 = Some(val);
},
7 => {
let val = Signature::read_from_in_protocol(i_prot)?;
f_7 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("Commit.parents", &f_1)?;
verify_required_field_exists("Commit.predecessors", &f_2)?;
verify_required_field_exists("Commit.root_tree", &f_3)?;
verify_required_field_exists("Commit.change_id", &f_4)?;
verify_required_field_exists("Commit.description", &f_5)?;
verify_required_field_exists("Commit.author", &f_6)?;
verify_required_field_exists("Commit.committer", &f_7)?;
let ret = Commit {
parents: f_1.expect("auto-generated code should have checked for presence of required fields"),
predecessors: f_2.expect("auto-generated code should have checked for presence of required fields"),
root_tree: f_3.expect("auto-generated code should have checked for presence of required fields"),
change_id: f_4.expect("auto-generated code should have checked for presence of required fields"),
description: f_5.expect("auto-generated code should have checked for presence of required fields"),
author: f_6.expect("auto-generated code should have checked for presence of required fields"),
committer: f_7.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("Commit");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("parents", TType::List, 1))?;
o_prot.write_list_begin(&TListIdentifier::new(TType::String, self.parents.len() as i32))?;
for e in &self.parents {
o_prot.write_bytes(e)?;
}
o_prot.write_list_end()?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("predecessors", TType::List, 2))?;
o_prot.write_list_begin(&TListIdentifier::new(TType::String, self.predecessors.len() as i32))?;
for e in &self.predecessors {
o_prot.write_bytes(e)?;
}
o_prot.write_list_end()?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("root_tree", TType::String, 3))?;
o_prot.write_bytes(&self.root_tree)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("change_id", TType::String, 4))?;
o_prot.write_bytes(&self.change_id)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("description", TType::String, 5))?;
o_prot.write_string(&self.description)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("author", TType::Struct, 6))?;
self.author.write_to_out_protocol(o_prot)?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("committer", TType::Struct, 7))?;
self.committer.write_to_out_protocol(o_prot)?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// ConflictPart
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ConflictPart {
pub content: TreeValue,
}
impl ConflictPart {
pub fn new(content: TreeValue) -> ConflictPart {
ConflictPart {
content,
}
}
}
impl TSerializable for ConflictPart {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<ConflictPart> {
i_prot.read_struct_begin()?;
let mut f_1: Option<TreeValue> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let val = TreeValue::read_from_in_protocol(i_prot)?;
f_1 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("ConflictPart.content", &f_1)?;
let ret = ConflictPart {
content: f_1.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("ConflictPart");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("content", TType::Struct, 1))?;
self.content.write_to_out_protocol(o_prot)?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}
//
// Conflict
//
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Conflict {
pub removes: Vec<ConflictPart>,
pub adds: Vec<ConflictPart>,
}
impl Conflict {
pub fn new(removes: Vec<ConflictPart>, adds: Vec<ConflictPart>) -> Conflict {
Conflict {
removes,
adds,
}
}
}
impl TSerializable for Conflict {
fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result<Conflict> {
i_prot.read_struct_begin()?;
let mut f_1: Option<Vec<ConflictPart>> = None;
let mut f_2: Option<Vec<ConflictPart>> = None;
loop {
let field_ident = i_prot.read_field_begin()?;
if field_ident.field_type == TType::Stop {
break;
}
let field_id = field_id(&field_ident)?;
match field_id {
1 => {
let list_ident = i_prot.read_list_begin()?;
let mut val: Vec<ConflictPart> = Vec::with_capacity(list_ident.size as usize);
for _ in 0..list_ident.size {
let list_elem_3 = ConflictPart::read_from_in_protocol(i_prot)?;
val.push(list_elem_3);
}
i_prot.read_list_end()?;
f_1 = Some(val);
},
2 => {
let list_ident = i_prot.read_list_begin()?;
let mut val: Vec<ConflictPart> = Vec::with_capacity(list_ident.size as usize);
for _ in 0..list_ident.size {
let list_elem_4 = ConflictPart::read_from_in_protocol(i_prot)?;
val.push(list_elem_4);
}
i_prot.read_list_end()?;
f_2 = Some(val);
},
_ => {
i_prot.skip(field_ident.field_type)?;
},
};
i_prot.read_field_end()?;
}
i_prot.read_struct_end()?;
verify_required_field_exists("Conflict.removes", &f_1)?;
verify_required_field_exists("Conflict.adds", &f_2)?;
let ret = Conflict {
removes: f_1.expect("auto-generated code should have checked for presence of required fields"),
adds: f_2.expect("auto-generated code should have checked for presence of required fields"),
};
Ok(ret)
}
fn write_to_out_protocol(&self, o_prot: &mut dyn TOutputProtocol) -> thrift::Result<()> {
let struct_ident = TStructIdentifier::new("Conflict");
o_prot.write_struct_begin(&struct_ident)?;
o_prot.write_field_begin(&TFieldIdentifier::new("removes", TType::List, 1))?;
o_prot.write_list_begin(&TListIdentifier::new(TType::Struct, self.removes.len() as i32))?;
for e in &self.removes {
e.write_to_out_protocol(o_prot)?;
}
o_prot.write_list_end()?;
o_prot.write_field_end()?;
o_prot.write_field_begin(&TFieldIdentifier::new("adds", TType::List, 2))?;
o_prot.write_list_begin(&TListIdentifier::new(TType::Struct, self.adds.len() as i32))?;
for e in &self.adds {
e.write_to_out_protocol(o_prot)?;
}
o_prot.write_list_end()?;
o_prot.write_field_end()?;
o_prot.write_field_stop()?;
o_prot.write_struct_end()
}
}

View file

@ -0,0 +1,64 @@
// Copyright 2020 Google LLC
//
// 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.
struct NormalFile {
1: required binary id,
2: required bool executable,
}
union TreeValue {
1: NormalFile normal_file,
2: binary symlink_id,
3: binary tree_id,
4: binary conflict_id,
}
struct TreeEntry {
1: required string name,
2: required TreeValue value,
}
struct Tree {
1: required list<TreeEntry> entries,
}
struct Timestamp {
1: required i64 millis_since_epoch,
2: required i32 tz_offset,
}
struct Signature {
1: required string name,
2: required string email,
3: required Timestamp timestamp,
}
struct Commit {
1: required list<binary> parents,
2: required list<binary> predecessors,
3: required binary root_tree,
4: required binary change_id,
5: required string description,
6: required Signature author,
7: required Signature committer,
}
struct ConflictPart {
1: required TreeValue content,
}
struct Conflict {
1: required list<ConflictPart> removes,
2: required list<ConflictPart> adds,
}