hashtree-core 0.2.81

Simple content-addressed merkle tree with KV storage
Documentation
use std::collections::HashMap;

use crate::types::{Link, LinkType, TreeNode};

#[derive(Clone)]
pub(crate) struct DirectoryFanoutSpan {
    pub link: Link,
    pub count: usize,
    pub first: String,
    pub last: String,
}

pub(crate) fn directory_fanout_meta(
    count: usize,
    first: &str,
    last: &str,
) -> HashMap<String, serde_json::Value> {
    let mut meta = HashMap::new();
    meta.insert("count".to_string(), serde_json::json!(count as u64));
    meta.insert("first".to_string(), serde_json::json!(first));
    meta.insert("last".to_string(), serde_json::json!(last));
    meta
}

fn internal_chunk_start(name: &str) -> Option<usize> {
    let suffix = name.strip_prefix("_chunk_")?;
    if suffix.is_empty() || !suffix.bytes().all(|byte| byte.is_ascii_digit()) {
        return None;
    }
    suffix.parse().ok()
}

fn node_uses_legacy_directory_fanout(node: &TreeNode) -> bool {
    node.node_type == LinkType::Dir
        && !node.links.is_empty()
        && node.links.iter().all(|link| {
            let Some(name) = link.name.as_deref() else {
                return false;
            };
            internal_chunk_start(name).is_some() && link.link_type == LinkType::Dir
        })
}

pub(crate) fn is_internal_directory_link(node: &TreeNode, link: &Link) -> bool {
    if node.node_type == LinkType::Fanout {
        return matches!(link.link_type, LinkType::Dir | LinkType::Fanout);
    }

    if !node_uses_legacy_directory_fanout(node) || link.link_type != LinkType::Dir {
        return false;
    }

    link.name
        .as_deref()
        .and_then(internal_chunk_start)
        .is_some()
}