capability-skeleton 0.1.0

A Rust library for managing and building complex hierarchical tree structures such as skill trees. Supports serialization, error handling, and deep tree metrics.
Documentation
// ---------------- [ File: capability-skeleton/src/skeleton_node_builder.rs ]
crate::ix!();

/// A builder so you can still do e.g.:
///
/// ```ignore
/// let node = SkeletonNodeBuilder::default()
///     .id(3)
///     .child_ids(vec![4,5])
///     .name("SomeAggregate")
///     .build(NodeKind::Aggregate) // chooses the Aggregate variant
///     .unwrap();
/// ```
#[derive(Debug, Default)]
pub struct SkeletonNodeBuilder {
    id:           Option<u16>,
    parent_id:    Option<Option<u16>>,
    child_ids:    Option<Vec<u16>>,
    leaf_count:   Option<u16>,
    capstone:     Option<bool>,
    name:         Option<String>,
    original_key: Option<String>,
    ordering:     Option<Option<SubBranchOrdering>>,
}

/// A small enum controlling which variant we build (Dispatch vs Aggregate vs LeafHolder).
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NodeKind {
    Dispatch,
    Aggregate,
    LeafHolder,
}

impl SkeletonNodeBuilder {
    /// Decide which variant to produce by passing a `NodeKind`.
    /// - For `LeafHolder`, we use `leaf_count` + `capstone` and ignore `child_ids`.
    /// - For `Dispatch` or `Aggregate`, we use `child_ids`, ignore `leaf_count`/`capstone`.
    pub fn build(self, kind: NodeKind) -> Result<SkeletonNode, derive_builder::UninitializedFieldError> {
        let id_val = self.id.unwrap_or_default();
        let parent_val = self.parent_id.unwrap_or_default();
        let cids = self.child_ids.unwrap_or_default();
        let leaves = self.leaf_count.unwrap_or_default();
        let cap = self.capstone.unwrap_or(false);
        let nm = self.name.unwrap_or_default();
        let orig = self.original_key.unwrap_or_default();
        let ord = self.ordering.unwrap_or_default();

        // Ensure name is not empty
        if nm.is_empty() {
            tracing::trace!("SkeletonNodeBuilder => missing required 'name'");
            return Err(derive_builder::UninitializedFieldError::new("name"));
        }
        let final_orig = if orig.is_empty() { nm.clone() } else { orig };

        match kind {
            NodeKind::Dispatch => Ok(SkeletonNode::Dispatch {
                id: id_val,
                parent_id: parent_val,
                child_ids: cids,
                name: nm,
                original_key: final_orig,
                ordering: ord,
            }),
            NodeKind::Aggregate => Ok(SkeletonNode::Aggregate {
                id: id_val,
                parent_id: parent_val,
                child_ids: cids,
                name: nm,
                original_key: final_orig,
                ordering: ord,
            }),
            NodeKind::LeafHolder => Ok(SkeletonNode::LeafHolder {
                id: id_val,
                parent_id: parent_val,
                leaf_count: leaves,
                capstone: cap,
                name: nm,
                original_key: final_orig,
                ordering: ord,
            }),
        }
    }

    // Usual chainable setters:
    pub fn id(mut self, val: u16) -> Self {
        self.id = Some(val);
        self
    }
    pub fn parent_id(mut self, val: Option<u16>) -> Self {
        self.parent_id = Some(val);
        self
    }
    pub fn child_ids(mut self, val: Vec<u16>) -> Self {
        self.child_ids = Some(val);
        self
    }
    pub fn leaf_count(mut self, val: u16) -> Self {
        self.leaf_count = Some(val);
        self
    }
    pub fn capstone(mut self, val: bool) -> Self {
        self.capstone = Some(val);
        self
    }
    pub fn name(mut self, val: impl Into<String>) -> Self {
        self.name = Some(val.into());
        self
    }
    pub fn original_key(mut self, val: impl Into<String>) -> Self {
        self.original_key = Some(val.into());
        self
    }
    pub fn ordering(mut self, val: Option<SubBranchOrdering>) -> Self {
        self.ordering = Some(val);
        self
    }
}