use std::collections::BTreeMap;
use panproto_gat::SiteRename;
use panproto_mig::Migration;
use panproto_schema::Schema;
use serde::{Deserialize, Serialize};
use crate::ObjectId;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Object {
Schema(Box<Schema>),
Migration {
src: ObjectId,
tgt: ObjectId,
mapping: Migration,
},
Commit(CommitObject),
Tag(TagObject),
DataSet(DataSetObject),
Complement(ComplementObject),
Protocol(Box<panproto_schema::Protocol>),
Expr(Box<panproto_expr::Expr>),
EditLog(EditLogObject),
Theory(Box<panproto_gat::Theory>),
TheoryMorphism(Box<panproto_gat::TheoryMorphism>),
}
impl Object {
#[must_use]
pub const fn type_name(&self) -> &'static str {
match self {
Self::Schema(_) => "schema",
Self::Migration { .. } => "migration",
Self::Commit(_) => "commit",
Self::Tag(_) => "tag",
Self::DataSet(_) => "dataset",
Self::Complement(_) => "complement",
Self::Protocol(_) => "protocol",
Self::Expr(_) => "expr",
Self::EditLog(_) => "editlog",
Self::Theory(_) => "theory",
Self::TheoryMorphism(_) => "theory_morphism",
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CommitObject {
pub schema_id: ObjectId,
pub parents: Vec<ObjectId>,
pub migration_id: Option<ObjectId>,
pub protocol: String,
pub author: String,
pub timestamp: u64,
pub message: String,
pub renames: Vec<SiteRename>,
pub protocol_id: Option<ObjectId>,
pub data_ids: Vec<ObjectId>,
pub complement_ids: Vec<ObjectId>,
pub edit_log_ids: Vec<ObjectId>,
pub theory_ids: BTreeMap<String, ObjectId>,
}
impl CommitObject {
#[must_use]
pub fn builder(
schema_id: ObjectId,
protocol: impl Into<String>,
author: impl Into<String>,
message: impl Into<String>,
) -> CommitObjectBuilder {
CommitObjectBuilder {
schema_id,
parents: Vec::new(),
migration_id: None,
protocol: protocol.into(),
author: author.into(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
message: message.into(),
renames: Vec::new(),
protocol_id: None,
data_ids: Vec::new(),
complement_ids: Vec::new(),
edit_log_ids: Vec::new(),
theory_ids: BTreeMap::new(),
}
}
}
pub struct CommitObjectBuilder {
schema_id: ObjectId,
parents: Vec<ObjectId>,
migration_id: Option<ObjectId>,
protocol: String,
author: String,
timestamp: u64,
message: String,
renames: Vec<SiteRename>,
protocol_id: Option<ObjectId>,
data_ids: Vec<ObjectId>,
complement_ids: Vec<ObjectId>,
edit_log_ids: Vec<ObjectId>,
theory_ids: BTreeMap<String, ObjectId>,
}
impl CommitObjectBuilder {
#[must_use]
pub fn parents(mut self, parents: Vec<ObjectId>) -> Self {
self.parents = parents;
self
}
#[must_use]
pub const fn migration_id(mut self, id: ObjectId) -> Self {
self.migration_id = Some(id);
self
}
#[must_use]
pub const fn timestamp(mut self, ts: u64) -> Self {
self.timestamp = ts;
self
}
#[must_use]
pub fn renames(mut self, renames: Vec<SiteRename>) -> Self {
self.renames = renames;
self
}
#[must_use]
pub const fn protocol_id(mut self, id: ObjectId) -> Self {
self.protocol_id = Some(id);
self
}
#[must_use]
pub fn data_ids(mut self, ids: Vec<ObjectId>) -> Self {
self.data_ids = ids;
self
}
#[must_use]
pub fn complement_ids(mut self, ids: Vec<ObjectId>) -> Self {
self.complement_ids = ids;
self
}
#[must_use]
pub fn edit_log_ids(mut self, ids: Vec<ObjectId>) -> Self {
self.edit_log_ids = ids;
self
}
#[must_use]
pub fn theory_ids(mut self, ids: BTreeMap<String, ObjectId>) -> Self {
self.theory_ids = ids;
self
}
#[must_use]
pub fn build(self) -> CommitObject {
CommitObject {
schema_id: self.schema_id,
parents: self.parents,
migration_id: self.migration_id,
protocol: self.protocol,
author: self.author,
timestamp: self.timestamp,
message: self.message,
renames: self.renames,
protocol_id: self.protocol_id,
data_ids: self.data_ids,
complement_ids: self.complement_ids,
edit_log_ids: self.edit_log_ids,
theory_ids: self.theory_ids,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DataSetObject {
pub schema_id: ObjectId,
pub data: Vec<u8>,
pub record_count: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ComplementObject {
pub migration_id: ObjectId,
pub data_id: ObjectId,
pub complement: Vec<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EditLogObject {
pub schema_id: ObjectId,
pub data_id: ObjectId,
pub edits: Vec<u8>,
pub edit_count: u64,
pub final_complement: ObjectId,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TagObject {
pub target: ObjectId,
pub tagger: String,
pub timestamp: u64,
pub message: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dataset_round_trip() -> Result<(), Box<dyn std::error::Error>> {
let ds = DataSetObject {
schema_id: ObjectId::ZERO,
data: vec![1, 2, 3, 4],
record_count: 42,
};
let bytes = rmp_serde::to_vec(&ds)?;
let ds2: DataSetObject = rmp_serde::from_slice(&bytes)?;
assert_eq!(ds.schema_id, ds2.schema_id);
assert_eq!(ds.data, ds2.data);
assert_eq!(ds.record_count, ds2.record_count);
Ok(())
}
#[test]
fn complement_round_trip() -> Result<(), Box<dyn std::error::Error>> {
let comp = ComplementObject {
migration_id: ObjectId::from_bytes([1; 32]),
data_id: ObjectId::from_bytes([2; 32]),
complement: vec![10, 20, 30],
};
let bytes = rmp_serde::to_vec(&comp)?;
let comp2: ComplementObject = rmp_serde::from_slice(&bytes)?;
assert_eq!(comp.migration_id, comp2.migration_id);
assert_eq!(comp.data_id, comp2.data_id);
assert_eq!(comp.complement, comp2.complement);
Ok(())
}
#[test]
fn edit_log_round_trip() -> Result<(), Box<dyn std::error::Error>> {
let el = EditLogObject {
schema_id: ObjectId::from_bytes([1; 32]),
data_id: ObjectId::from_bytes([2; 32]),
edits: vec![42, 43, 44],
edit_count: 3,
final_complement: ObjectId::from_bytes([3; 32]),
};
let bytes = rmp_serde::to_vec(&el)?;
let el2: EditLogObject = rmp_serde::from_slice(&bytes)?;
assert_eq!(el.schema_id, el2.schema_id);
assert_eq!(el.data_id, el2.data_id);
assert_eq!(el.edits, el2.edits);
assert_eq!(el.edit_count, el2.edit_count);
assert_eq!(el.final_complement, el2.final_complement);
Ok(())
}
#[test]
fn commit_with_edit_logs() -> Result<(), Box<dyn std::error::Error>> {
let commit = CommitObject::builder(ObjectId::ZERO, "test", "test", "test")
.timestamp(0)
.edit_log_ids(vec![
ObjectId::from_bytes([10; 32]),
ObjectId::from_bytes([11; 32]),
])
.build();
let bytes = rmp_serde::to_vec(&commit)?;
let commit2: CommitObject = rmp_serde::from_slice(&bytes)?;
assert_eq!(commit.edit_log_ids, commit2.edit_log_ids);
Ok(())
}
#[test]
fn commit_with_theory_ids() -> Result<(), Box<dyn std::error::Error>> {
let mut theories = BTreeMap::new();
theories.insert("ThGraph".to_owned(), ObjectId::from_bytes([5; 32]));
let commit = CommitObject::builder(ObjectId::ZERO, "test", "test", "test")
.timestamp(0)
.theory_ids(theories)
.build();
let bytes = rmp_serde::to_vec(&commit)?;
let commit2: CommitObject = rmp_serde::from_slice(&bytes)?;
assert_eq!(commit.theory_ids, commit2.theory_ids);
assert_eq!(
commit2.theory_ids.get("ThGraph"),
Some(&ObjectId::from_bytes([5; 32]))
);
Ok(())
}
#[test]
fn commit_backward_compat_no_theory_ids() -> Result<(), Box<dyn std::error::Error>> {
let commit_old = CommitObject::builder(ObjectId::ZERO, "test", "test", "test")
.timestamp(0)
.build();
let bytes = rmp_serde::to_vec(&commit_old)?;
let commit2: CommitObject = rmp_serde::from_slice(&bytes)?;
assert!(commit2.theory_ids.is_empty());
Ok(())
}
}