capability-skeleton-string 0.1.0

A robust library for managing skill tree skeletons using string-based nodes and bidirectional conversions between string and numeric formats.
Documentation
// ---------------- [ File: capability-skeleton-string/src/string_skeleton.rs ]
crate::ix!();

/// Use this object to specify our whole tree of generated data.
#[derive(AiJsonTemplateWithJustification,AiJsonTemplate,SaveLoad,Debug, Clone, PartialEq, Eq,Serialize, Deserialize, Builder, Default, Getters,MutGetters)]
#[builder(pattern="owned", setter(into))]
#[getset(get="pub",get_mut="pub")]
pub struct StringSkeleton {

    /// This field holds *verbatim* the lower-kebab-case target-name belonging to the tree grow process.
    #[builder(default = "\"target\".to_string()")]
    #[serde(default)]
    target_name: String,

    /// Set this field to specify the whole map of nodes within our tree.
    map: HashMap<String, StringSkeletonNode>,
}

impl FuzzyFromJsonValue for JustifiedStringSkeleton {

    fn fuzzy_from_json_value(value: &Value) -> Result<Self, FuzzyFromJsonValueError> {
        trace!("(JustifiedStringSkeleton) Entering fuzzy_from_json_value => {}", value);

        // Flatten top-level "fields" if present
        let mut top_cloned = value.clone();
        flatten_fields_if_present_in_value(&mut top_cloned);

        let obj = match top_cloned.as_object() {
            Some(o) => {
                let keys_collected: Vec<_> = o.keys().cloned().collect();
                trace!("(JustifiedStringSkeleton) Top-level keys after flatten => {:?}", keys_collected);
                o
            }
            None => {
                error!("(JustifiedStringSkeleton) Not an object => fail");
                return Err(FuzzyFromJsonValueError::NotAnObject {
                    target_type: "JustifiedStringSkeleton",
                    actual: top_cloned,
                });
            }
        };

        let target_name_val = if let Some(raw) = obj.get("target_name") {
            raw.to_string().to_kebab_case()
        } else {
            warn!("(JustifiedGrowerTreeConfiguration) 'depth' missing => default 0");
            "target-name-unset".to_string()
        };

        let target_name_conf = get_f64_field(&obj, "target_name_confidence", "JustifiedGrowerTreeConfiguration").unwrap_or(0.0);
        let target_name_just = get_string_field(&obj, "target_name_justification", "JustifiedGrowerTreeConfiguration").unwrap_or_default();

        // Must have "map" (the object containing our nodes)
        let map_val = obj.get("map").ok_or_else(|| {
            error!("(JustifiedStringSkeleton) Missing 'map' => fail");
            FuzzyFromJsonValueError::MissingField {
                field_name: "map",
                target_type: "JustifiedStringSkeleton",
            }
        })?;
        let map_obj = match map_val.as_object() {
            Some(m) => {
                let map_keys_vec: Vec<_> = m.keys().cloned().collect();
                trace!("(JustifiedStringSkeleton) 'map' object => child keys => {:?}", map_keys_vec);
                m
            }
            None => {
                error!("(JustifiedStringSkeleton) 'map' must be object => fail");
                return Err(FuzzyFromJsonValueError::Other {
                    target_type: "JustifiedStringSkeleton",
                    detail: format!("'map' must be an object, got {:?}", map_val),
                });
            }
        };

        let mut final_map = HashMap::new();

        // Here we skip well‐known “schema” keys that are not actual domain nodes.
        // We already skip "map_key_template", "map_value_template", "generation_instructions".
        // Now we also skip "required" (bool), "type", "struct_docs", "variant_docs", etc. 
        // That way, anything like `"required": true` does not get parsed as a node name.
        for (k, v) in map_obj {
            // Skip known “schema” or “metadata” keys:
            if k == "generation_instructions"
                || k == "map_key_template"
                || k == "map_value_template"
                || k == "required"          // NEW
                || k == "type"             // also skip if it appears 
                || k == "struct_docs"      // skip if present
                || k == "variant_docs"     // skip if present
            {
                trace!("(JustifiedStringSkeleton) Skipping meta key='{}' => not a node", k);
                continue;
            }

            trace!("(JustifiedStringSkeleton) Parsing node key='{}' => {}", k, v);
            let node_just = match JustifiedStringSkeletonNode::fuzzy_from_json_value(v) {
                Ok(nj) => {
                    trace!("(JustifiedStringSkeleton) Successfully parsed node='{}' => Justified form", k);
                    nj
                }
                Err(e) => {
                    error!("(JustifiedStringSkeleton) Error parsing node='{}' => {:?}", k, e);
                    return Err(FuzzyFromJsonValueError::Other {
                        target_type: "JustifiedStringSkeleton",
                        detail: format!("Error parsing node for key='{}': {:?}", k, e),
                    });
                }
            };

            // Insert into final map if successfully parsed
            final_map.insert(k.clone(), node_just.into());
        }

        // parse map_confidence => f64
        let map_conf = get_f64_field(obj, "map_confidence", "JustifiedStringSkeleton")
            .unwrap_or(0.0);
        let map_just = get_string_field(obj, "map_justification", "JustifiedStringSkeleton")
            .unwrap_or_default();

        trace!("(JustifiedStringSkeleton) Successfully built final => map size={}", final_map.len());

        Ok(JustifiedStringSkeleton {
            target_name:               target_name_val,
            target_name_confidence:    target_name_conf,
            target_name_justification: target_name_just,
            map: final_map,
            map_confidence: map_conf,
            map_justification: map_just,
        })
    }
}