use holochain_core_types::{
chain_header::ChainHeader,
crud_status::CrudStatus,
entry::{entry_type::EntryType, Entry, EntryWithMeta},
time::Timeout,
};
use holochain_json_api::{error::JsonError, json::*};
use holochain_persistence_api::cas::content::{Address, AddressableContent};
use std::collections::HashMap;
#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)]
pub enum StatusRequestKind {
Initial,
Latest,
All,
}
impl Default for StatusRequestKind {
fn default() -> Self {
StatusRequestKind::Latest
}
}
#[derive(Deserialize, Debug, Serialize, DefaultJson, PartialEq, Clone)]
pub struct GetEntryOptions {
pub status_request: StatusRequestKind,
pub entry: bool,
pub headers: bool,
pub timeout: Timeout,
}
impl Default for GetEntryOptions {
fn default() -> Self {
GetEntryOptions {
status_request: StatusRequestKind::default(),
entry: true,
headers: false,
timeout: Default::default(),
}
}
}
impl GetEntryOptions {
pub fn new(
status_request: StatusRequestKind,
entry: bool,
headers: bool,
timeout: Timeout,
) -> Self {
GetEntryOptions {
status_request,
entry,
headers,
timeout,
}
}
}
#[derive(Deserialize, Debug, Serialize, DefaultJson)]
pub struct GetEntryArgs {
pub address: Address,
pub options: GetEntryOptions,
}
#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone)]
pub struct EntryResultMeta {
pub address: Address,
pub entry_type: EntryType,
pub crud_status: CrudStatus,
}
#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone)]
pub struct GetEntryResultItem {
pub meta: Option<EntryResultMeta>,
pub entry: Option<Entry>,
pub headers: Vec<ChainHeader>,
}
impl GetEntryResultItem {
pub fn new(maybe_entry_with_meta: Option<(&EntryWithMeta, Vec<ChainHeader>)>) -> Self {
match maybe_entry_with_meta {
Some((entry_with_meta, headers)) => GetEntryResultItem {
meta: Some(EntryResultMeta {
address: entry_with_meta.entry.address(),
entry_type: entry_with_meta.entry.entry_type(),
crud_status: entry_with_meta.crud_status,
}),
entry: Some(entry_with_meta.entry.clone()),
headers,
},
_ => GetEntryResultItem {
meta: None,
entry: None,
headers: Vec::new(),
},
}
}
}
#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, Default)]
pub struct EntryHistory {
pub items: Vec<GetEntryResultItem>,
pub crud_links: HashMap<Address, Address>,
}
impl EntryHistory {
pub fn new() -> Self {
Default::default()
}
pub fn push(&mut self, entry_with_meta: &EntryWithMeta, headers: Vec<ChainHeader>) {
let address = entry_with_meta.entry.address();
let item = GetEntryResultItem::new(Some((entry_with_meta, headers)));
self.items.push(item);
if let Some(new_address) = entry_with_meta.maybe_link_update_delete.clone() {
self.crud_links.insert(address, new_address);
}
}
}
#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone)]
#[allow(clippy::large_enum_variant)]
pub enum GetEntryResultType {
Single(GetEntryResultItem),
All(EntryHistory),
}
#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone)]
pub struct GetEntryResult {
pub result: GetEntryResultType,
}
impl GetEntryResult {
pub fn new(
request_kind: StatusRequestKind,
maybe_entry_with_meta_and_headers: Option<(&EntryWithMeta, Vec<ChainHeader>)>,
) -> Self {
match request_kind {
StatusRequestKind::All => {
let mut entry_result = GetEntryResult {
result: GetEntryResultType::All(EntryHistory::new()),
};
if let Some((entry_with_meta, headers)) = maybe_entry_with_meta_and_headers {
entry_result.push(entry_with_meta, headers);
}
entry_result
}
_ => GetEntryResult {
result: GetEntryResultType::Single(GetEntryResultItem::new(
maybe_entry_with_meta_and_headers,
)),
},
}
}
pub fn found(&self) -> bool {
match self.result {
GetEntryResultType::Single(ref item) => item.meta.is_some(),
GetEntryResultType::All(ref history) => !history.items.is_empty(),
}
}
pub fn clear(&mut self) {
match self.result {
GetEntryResultType::Single(_) => {
self.result = GetEntryResultType::Single(GetEntryResultItem::new(None))
}
GetEntryResultType::All(ref mut history) => history.items.clear(),
};
}
pub fn push(&mut self, entry_with_meta: &EntryWithMeta, headers: Vec<ChainHeader>) {
match self.result {
GetEntryResultType::Single(_) => {
self.result = GetEntryResultType::Single(GetEntryResultItem::new(Some((
entry_with_meta,
headers,
))))
}
GetEntryResultType::All(ref mut history) => history.push(entry_with_meta, headers),
};
}
pub fn latest(&self) -> Option<Entry> {
match self.result {
GetEntryResultType::Single(ref item) => item.entry.clone(),
GetEntryResultType::All(ref history) => {
let last = history.items.last()?;
last.entry.clone()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use holochain_core_types::{
chain_header::test_chain_header,
entry::{test_entry, test_entry_a, test_entry_b},
};
#[test]
fn test_get_entry_result_found() {
let result = GetEntryResult::new(StatusRequestKind::Initial, None);
assert!(!result.found());
let result = GetEntryResult::new(StatusRequestKind::Latest, None);
assert!(!result.found());
let result = GetEntryResult::new(StatusRequestKind::All, None);
assert!(!result.found());
}
#[test]
fn test_get_entry_single_latest() {
let mut result = GetEntryResult::new(StatusRequestKind::Initial, None);
assert_eq!(result.latest(), None);
result.push(
&EntryWithMeta {
entry: test_entry(),
crud_status: CrudStatus::Live,
maybe_link_update_delete: None,
},
vec![test_chain_header()],
);
assert!(result.found());
assert_eq!(result.latest(), Some(test_entry()));
}
#[test]
fn test_get_entry_all_latest() {
let mut result = GetEntryResult::new(StatusRequestKind::All, None);
assert_eq!(result.latest(), None);
result.push(
&EntryWithMeta {
entry: test_entry_a(),
crud_status: CrudStatus::Modified,
maybe_link_update_delete: None,
},
vec![test_chain_header()],
);
result.push(
&EntryWithMeta {
entry: test_entry_b(),
crud_status: CrudStatus::Live,
maybe_link_update_delete: None,
},
vec![test_chain_header()],
);
assert!(result.found());
assert_eq!(result.latest(), Some(test_entry_b()));
}
#[test]
fn test_clear() {
let mut result = GetEntryResult::new(StatusRequestKind::All, None);
result.push(
&EntryWithMeta {
entry: test_entry(),
crud_status: CrudStatus::Live,
maybe_link_update_delete: None,
},
vec![test_chain_header()],
);
assert!(result.found());
result.clear();
assert!(!result.found());
result = GetEntryResult::new(StatusRequestKind::Initial, None);
result.push(
&EntryWithMeta {
entry: test_entry(),
crud_status: CrudStatus::Live,
maybe_link_update_delete: None,
},
vec![test_chain_header()],
);
assert!(result.found());
result.clear();
assert!(!result.found());
}
}