rave_engine 0.8.0

A secure and efficient JSON Schema validation and Rhai script execution engine
Documentation
use hdk::prelude::*;

/// Generic structure to hold all versions of an entry
#[derive(Serialize, Deserialize, Debug)]
pub struct EntryWithAllVersions<T> {
    pub current_entry: T,
    pub other_versions: Vec<T>,
}

/// Generic utility function to get all versions of an entry by following the update chain
/// both backwards (older versions) and forwards (newer versions)
pub fn get_entry_all_versions<T, F>(
    hash: ActionHash,
    entry_converter: F,
) -> ExternResult<EntryWithAllVersions<T>>
where
    F: Fn(&ActionHash) -> ExternResult<T>,
{
    let details = hdk::prelude::get_details(hash.clone(), GetOptions::default())?;
    if let Some(Details::Record(record_details)) = details {
        // Get the current version
        let current_entry = entry_converter(&hash)?;

        // Collect all versions - both older and newer
        let mut older_versions = Vec::new();
        let mut newer_versions = Vec::new();

        // First, check if this record is itself an update and get older versions
        let current_record_details = record_details.clone();
        if let Action::Update(update_action) = current_record_details.record.action() {
            // This is an update, so there are older versions
            let mut prev_hash = update_action.original_action_address.clone();

            // Follow the prev_action chain backwards
            while let Some(Details::Record(prev_record_details)) =
                hdk::prelude::get_details(prev_hash.clone(), GetOptions::default())?
            {
                let prev_entry = entry_converter(&prev_hash)?;
                older_versions.push(prev_entry);

                // Check if this previous record is also an update
                if let Action::Update(prev_update_action) = prev_record_details.record.action() {
                    prev_hash = prev_update_action.original_action_address.clone();
                } else {
                    break; // Reached the original record
                }
            }

            // Reverse to get chronological order (oldest first)
            older_versions.reverse();
        }

        // Now follow the update chain forward for newer versions
        let mut current_details = record_details;

        // Follow the update chain iteratively
        loop {
            if !current_details.updates.is_empty() {
                if let Some(first_update) = current_details.updates.first() {
                    // Get the details of the first (and expected only) update
                    let update_hash = first_update.action_address();
                    let updated_entry = entry_converter(update_hash)?;
                    newer_versions.push(updated_entry);

                    // Get details of the updated record to check for further updates
                    if let Some(Details::Record(next_record_details)) =
                        hdk::prelude::get_details(update_hash.clone(), GetOptions::default())?
                    {
                        current_details = next_record_details;
                    } else {
                        break;
                    }
                } else {
                    break; // No more updates
                }
            } else {
                break; // No updates
            }
        }

        // Combine all versions in chronological order
        let mut all_versions = Vec::new();
        all_versions.extend(older_versions);
        all_versions.extend(newer_versions);

        return Ok(EntryWithAllVersions {
            current_entry,
            other_versions: all_versions,
        });
    }
    Err(wasm_error!("No entry found"))
}