1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use hdk::prelude::*;

use crate::retrieval::utils::*;

#[cfg(feature = "mock")]
use ::mockall::automock;

/// A triple of an Entry along with the HeaderHash
/// of that committed entry and the EntryHash of the entry
pub type EntryAndHash<T> = (T, HeaderHash, EntryHash);

/// The same as an EntryAndHash but inside an Option,
/// so it can be Some(...) or None
pub type OptionEntryAndHash<T> = Option<EntryAndHash<T>>;

#[derive(Debug, PartialEq, Clone)]
pub struct GetLatestEntry {}
#[cfg_attr(feature = "mock", automock)]
impl GetLatestEntry {
    /// If an entry at the `entry_hash` has multiple updates to itself, this
    /// function will sort through them by timestamp in order to return the contents
    /// of the latest update. It also has the special behaviour of returning the
    /// ORIGINAL HeaderHash, as opposed to the HeaderHash of the Header that performed
    /// that latest update. This is useful if you want hashes in your application
    /// to act consistently, almost acting as an "id" in a centralized system.
    /// It simplifies traversal of the update tree, since all updates
    /// made by the client can reference the original, instead of updates reference updates
    pub fn get_latest_for_entry<
        T: 'static + TryFrom<SerializedBytes, Error = SerializedBytesError>,
    >(
        &self,
        entry_hash: EntryHash,
        get_options: GetOptions,
    ) -> ExternResult<OptionEntryAndHash<T>> {
        // First, make sure we DO have the latest header_hash address
        let maybe_latest_header_hash = match get_details(entry_hash.clone(), get_options.clone())? {
            Some(Details::Entry(details)) => match details.entry_dht_status {
                metadata::EntryDhtStatus::Live => match details.updates.len() {
                    // pass out the header associated with this entry
                    0 => Some(get_header_hash(details.headers.first().unwrap().to_owned())),
                    _ => {
                        let mut sortlist = details.updates.to_vec();
                        // unix timestamp should work for sorting
                        sortlist.sort_by_key(|update| update.header().timestamp().as_millis());
                        // sorts in ascending order, so take the last element
                        let last = sortlist.last().unwrap().to_owned();
                        Some(get_header_hash(last))
                    }
                },
                metadata::EntryDhtStatus::Dead => None,
                _ => None,
            },
            _ => None,
        };

        // Second, go and get that element, and return it and its header_address
        match maybe_latest_header_hash {
            Some(latest_header_hash) => match get(latest_header_hash, get_options)? {
                Some(element) => match element.entry().to_app_option::<T>()? {
                    Some(entry) => Ok(Some((
                        entry,
                        match element.header() {
                            // we DO want to return the header for the original
                            // instead of the updated, in our case
                            Header::Update(update) => update.original_header_address.clone(),
                            Header::Create(_) => element.header_address().clone(),
                            _ => {
                                unreachable!("Can't have returned a header for a nonexistent entry")
                            }
                        },
                        element.header().entry_hash().unwrap().to_owned(),
                    ))),
                    None => Ok(None),
                },
                None => Ok(None),
            },
            None => Ok(None),
        }
    }
}