diff --git a/CHANGELOG.md b/CHANGELOG.md index 197e3e976..91f184fba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 9c12e8323..39f01372a 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -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; diff --git a/lib/src/local_backend.rs b/lib/src/local_backend.rs index 353bf0721..8d6e60ad6 100644 --- a/lib/src/local_backend.rs +++ b/lib/src/local_backend.rs @@ -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 for BackendError { @@ -43,8 +44,8 @@ impl From for BackendError { } } -impl From for BackendError { - fn from(err: protobuf::Error) -> Self { +impl From 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 { 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 { 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 { 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 { 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 { 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(input: &mut impl Read) -> BackendResult { + 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| CommitId::new(parent.clone()); - let parents = proto.parents.iter().map(commit_id_from_proto).collect(); - let predecessors = proto +fn write_thrift(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| 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 { diff --git a/lib/src/local_backend_model.rs b/lib/src/local_backend_model.rs new file mode 100644 index 000000000..e709cfa57 --- /dev/null +++ b/lib/src/local_backend_model.rs @@ -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, + pub executable: bool, +} + +impl NormalFile { + pub fn new(id: Vec, executable: bool) -> NormalFile { + NormalFile { + id, + executable, + } + } +} + +impl TSerializable for NormalFile { + fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { + i_prot.read_struct_begin()?; + let mut f_1: Option> = None; + let mut f_2: Option = 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), + TreeId(Vec), + ConflictId(Vec), +} + +impl TSerializable for TreeValue { + fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { + let mut ret: Option = 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 { + i_prot.read_struct_begin()?; + let mut f_1: Option = None; + let mut f_2: Option = 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, +} + +impl Tree { + pub fn new(entries: Vec) -> Tree { + Tree { + entries, + } + } +} + +impl TSerializable for Tree { + fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { + i_prot.read_struct_begin()?; + let mut f_1: Option> = 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::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 { + i_prot.read_struct_begin()?; + let mut f_1: Option = None; + let mut f_2: Option = 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 { + i_prot.read_struct_begin()?; + let mut f_1: Option = None; + let mut f_2: Option = None; + let mut f_3: Option = 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>, + pub predecessors: Vec>, + pub root_tree: Vec, + pub change_id: Vec, + pub description: String, + pub author: Signature, + pub committer: Signature, +} + +impl Commit { + pub fn new(parents: Vec>, predecessors: Vec>, root_tree: Vec, change_id: Vec, 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 { + i_prot.read_struct_begin()?; + let mut f_1: Option>> = None; + let mut f_2: Option>> = None; + let mut f_3: Option> = None; + let mut f_4: Option> = None; + let mut f_5: Option = None; + let mut f_6: Option = None; + let mut f_7: Option = 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::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::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 { + i_prot.read_struct_begin()?; + let mut f_1: Option = 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, + pub adds: Vec, +} + +impl Conflict { + pub fn new(removes: Vec, adds: Vec) -> Conflict { + Conflict { + removes, + adds, + } + } +} + +impl TSerializable for Conflict { + fn read_from_in_protocol(i_prot: &mut dyn TInputProtocol) -> thrift::Result { + i_prot.read_struct_begin()?; + let mut f_1: Option> = None; + let mut f_2: Option> = 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::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 = 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() + } +} + diff --git a/lib/src/local_backend_model.thrift b/lib/src/local_backend_model.thrift new file mode 100644 index 000000000..ed1dceb36 --- /dev/null +++ b/lib/src/local_backend_model.thrift @@ -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 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 parents, + 2: required list 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 removes, + 2: required list adds, +}