use std::convert::TryFrom;
use std::slice::Iter;
use crate::document::DocumentViewId;
use crate::entry::{decode_entry, EntrySigned, LogId, SeqNum};
use crate::hash::Hash;
use crate::identity::Author;
use crate::operation::{AsOperation, Operation, OperationEncoded};
use crate::schema::SchemaId;
#[derive(Clone, Debug)]
pub struct LogEntry {
pub entry_encoded: EntrySigned,
pub operation_encoded: OperationEncoded,
}
impl LogEntry {
pub fn new(entry_encoded: &EntrySigned, operation_encoded: &OperationEncoded) -> Self {
Self {
entry_encoded: entry_encoded.to_owned(),
operation_encoded: operation_encoded.to_owned(),
}
}
pub fn author(&self) -> String {
self.entry_encoded.author().as_str().to_string()
}
pub fn hash(&self) -> Hash {
self.entry_encoded.hash()
}
pub fn hash_str(&self) -> String {
self.entry_encoded.hash().as_str().to_string()
}
pub fn operation(&self) -> Operation {
Operation::try_from(&self.operation_encoded).unwrap()
}
pub fn entry_encoded(&self) -> EntrySigned {
self.entry_encoded.clone()
}
pub fn operation_encoded(&self) -> OperationEncoded {
self.operation_encoded.clone()
}
pub fn previous_operations(&self) -> Option<DocumentViewId> {
self.operation().previous_operations()
}
}
#[derive(Debug, Clone)]
pub struct Log {
author: Author,
log_id: LogId,
document: Hash,
schema: SchemaId,
entries: Vec<LogEntry>,
}
impl Log {
pub fn new(
document_id: Hash,
entry_signed: &EntrySigned,
operation_encoded: &OperationEncoded,
) -> Self {
let entry = decode_entry(entry_signed, Some(operation_encoded)).unwrap();
let mut log = Self {
author: entry_signed.author(),
log_id: entry.log_id().to_owned(),
document: document_id,
schema: entry.operation().unwrap().schema(),
entries: Vec::new(),
};
log.add_entry(LogEntry::new(entry_signed, operation_encoded));
log
}
pub fn entries(&self) -> Vec<LogEntry> {
self.entries.to_owned()
}
pub fn author(&self) -> Author {
self.author.to_owned()
}
pub fn id(&self) -> LogId {
self.log_id.to_owned()
}
pub fn schema(&self) -> SchemaId {
self.schema.to_owned()
}
pub fn document(&self) -> Hash {
self.document.to_owned()
}
pub fn add_entry(&mut self, entry: LogEntry) {
self.entries.push(entry)
}
pub fn next_seq_num(&self) -> SeqNum {
SeqNum::new((self.entries.len() + 1) as u64).unwrap()
}
}
#[derive(Clone, Debug)]
pub struct AuthorLogs(Vec<Log>);
impl AuthorLogs {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn create_new_log(
&mut self,
document_id: Hash,
entry_signed: &EntrySigned,
operation_encoded: &OperationEncoded,
) {
self.0
.push(Log::new(document_id, entry_signed, operation_encoded))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> Iter<Log> {
self.0.iter()
}
pub fn get_log_by_document_id(&self, document_id: &Hash) -> Option<&Log> {
self.0.iter().find(|log| log.document() == *document_id)
}
pub fn next_log_id(&self) -> LogId {
LogId::new((self.0.len() + 1) as u64)
}
pub fn get_document_log_id(&self, document_id: &Hash) -> LogId {
let document_log = self.iter().find(|log| log.document() == *document_id);
match document_log {
Some(log) => log.id(),
None => self.next_log_id(),
}
}
pub fn find_document_log_by_entry(&self, entry: &Hash) -> Option<&Log> {
self.0.iter().find(|log| {
log.entries()
.iter()
.any(|log_entry| log_entry.hash() == *entry)
})
}
pub fn get_log_mut(&mut self, id: &LogId) -> Option<&mut Log> {
self.0.iter_mut().find(|log| log.id() == *id)
}
}
impl Default for AuthorLogs {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use crate::entry::{decode_entry, EntrySigned, LogId};
use crate::operation::{AsOperation, OperationEncoded};
use crate::test_utils::fixtures::{entry_signed_encoded, operation_encoded};
use super::{AuthorLogs, Log, LogEntry};
#[rstest]
fn log_entry(entry_signed_encoded: EntrySigned, operation_encoded: OperationEncoded) {
let entry = decode_entry(&entry_signed_encoded, Some(&operation_encoded)).unwrap();
let log_entry = LogEntry::new(&entry_signed_encoded, &operation_encoded);
assert_eq!(log_entry.entry_encoded(), entry_signed_encoded);
assert_eq!(
log_entry.operation_encoded().hash(),
operation_encoded.hash()
);
assert_eq!(log_entry.author(), entry_signed_encoded.author().as_str());
assert_eq!(log_entry.hash(), entry_signed_encoded.hash());
assert_eq!(
log_entry.operation().schema(),
entry.operation().unwrap().schema()
);
}
#[rstest]
fn log(entry_signed_encoded: EntrySigned, operation_encoded: OperationEncoded) {
let entry = decode_entry(&entry_signed_encoded, Some(&operation_encoded)).unwrap();
let log = Log::new(
entry_signed_encoded.hash(),
&entry_signed_encoded,
&operation_encoded,
);
assert_eq!(log.entries().len(), 1);
assert_eq!(log.next_seq_num().as_u64(), 2);
assert_eq!(log.author(), entry_signed_encoded.author());
assert_eq!(log.document(), entry_signed_encoded.hash());
assert_eq!(log.schema(), entry.operation().unwrap().schema());
}
#[rstest]
fn author_logs(entry_signed_encoded: EntrySigned, operation_encoded: OperationEncoded) {
let mut author_logs = AuthorLogs::new();
author_logs.create_new_log(
entry_signed_encoded.hash(),
&entry_signed_encoded,
&operation_encoded,
);
assert_eq!(author_logs.len(), 1);
assert_eq!(
author_logs
.get_log_by_document_id(&entry_signed_encoded.hash())
.unwrap()
.id(),
LogId::new(1)
);
assert_eq!(author_logs.next_log_id(), LogId::new(2));
assert_eq!(
author_logs.get_document_log_id(&entry_signed_encoded.hash()),
LogId::new(1)
);
assert_eq!(
author_logs
.find_document_log_by_entry(&entry_signed_encoded.hash())
.unwrap()
.id(),
LogId::new(1)
);
}
}