soma-core 2.0.0

World's first production-ready self-aware development system with meta-cognitive capabilities and cognitive reasoning engine for intelligent development platforms
Documentation
#![allow(dead_code)] // Keep comprehensive API for future expansion

use chrono::{DateTime, Utc};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::time::{Duration as StdDuration, SystemTime};

/// SymbolicContext for SOMA: variable memory and path resolution
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SymbolicContext {
    memory: HashMap<String, String>,
    children: HashMap<String, SymbolicContext>,
    last_modified: HashMap<String, DateTime<Utc>>,
    key_meta: HashMap<String, SymbolicKeyMeta>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SymbolicKeyMeta {
    pub last_modified: DateTime<Utc>,
    pub ttl: Option<StdDuration>,
    pub origin: Option<String>,
    pub phase: Option<String>,
    pub source_agent: Option<String>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct SymbolicDiff {
    pub added: HashMap<String, String>,
    pub removed: HashMap<String, String>,
    pub changed: HashMap<String, (String, String)>, // (old, new)
}

pub trait SymbolicObserver {
    fn on_set(&self, key: &str, value: &str);
    fn on_merge(&self, other: &SymbolicContext);
    fn on_resolve(&self, path: &str, result: Option<&str>);
}

impl SymbolicContext {
    pub fn new() -> Self {
        Self {
            memory: HashMap::new(),
            children: HashMap::new(),
            last_modified: HashMap::new(),
            key_meta: HashMap::new(),
        }
    }

    pub fn set(&mut self, key: &str, value: &str) {
        self.memory.insert(key.to_string(), value.to_string());
        let now = Utc::now();
        self.last_modified.insert(key.to_string(), now);
        self.key_meta.insert(
            key.to_string(),
            SymbolicKeyMeta {
                last_modified: now,
                ttl: None,
                origin: None,
                phase: None,
                source_agent: None,
            },
        );
    }

    pub fn set_with_meta(&mut self, key: &str, value: &str, meta: SymbolicKeyMeta) {
        self.memory.insert(key.to_string(), value.to_string());
        self.last_modified
            .insert(key.to_string(), meta.last_modified);
        self.key_meta.insert(key.to_string(), meta);
    }

    pub fn merge(&mut self, other: &SymbolicContext) {
        for (k, v) in &other.memory {
            self.memory.insert(k.clone(), v.clone());
        }
    }

    pub fn get(&self, key: &str) -> Option<&String> {
        self.memory.get(key)
    }

    pub fn set_child(&mut self, name: &str, ctx: SymbolicContext) {
        self.children.insert(name.to_string(), ctx);
    }

    pub fn get_child(&self, name: &str) -> Option<&SymbolicContext> {
        self.children.get(name)
    }

    /// Resolves a nested dotted path, traversing children if needed
    pub fn resolve_path(&self, path: &str) -> Option<String> {
        let result = {
            let mut parts = path.split('.');
            let first = parts.next()?;
            if let Some(child) = self.children.get(first) {
                return child.resolve_path(&parts.collect::<Vec<_>>().join("."));
            }
            let current = self.memory.get(first)?;
            let mut value: Value = match serde_json::from_str(current) {
                Ok(json) => json,
                Err(_) => Value::String(current.clone()),
            };
            for part in parts {
                match &value {
                    Value::Object(map) => {
                        value = map.get(part)?.clone();
                    }
                    _ => return None,
                }
            }
            match value {
                Value::String(s) => Some(s),
                v => Some(v.to_string()),
            }
        };
        result
    }

    pub fn enter_scope(&mut self, scope: &str) -> &mut SymbolicContext {
        self.children
            .entry(scope.to_string())
            .or_insert_with(SymbolicContext::new)
    }

    pub fn set_ttl(&mut self, key: &str, ttl: StdDuration) {
        if let Some(meta) = self.key_meta.get_mut(key) {
            meta.ttl = Some(ttl);
        }
    }

    pub fn cleanup_expired(&mut self) {
        let now = SystemTime::now();
        let expired: Vec<_> = self
            .key_meta
            .iter()
            .filter_map(|(k, meta)| {
                if let Some(ttl) = meta.ttl {
                    let lm = meta.last_modified.with_timezone(&Utc);
                    let lm_sys = lm.into();
                    if now.duration_since(lm_sys).unwrap_or(StdDuration::ZERO) > ttl {
                        return Some(k.clone());
                    }
                }
                None
            })
            .collect();
        for k in expired {
            self.memory.remove(&k);
            self.key_meta.remove(&k);
        }
    }

    /// Returns a flattened HashMap with dot notation keys
    pub fn flatten(&self) -> HashMap<String, String> {
        let mut flat = self.memory.clone();
        for (child_name, child_ctx) in &self.children {
            for (k, v) in child_ctx.flatten() {
                flat.insert(format!("{}.{}", child_name, k), v);
            }
        }
        flat
    }

    /// Computes the diff between two SymbolicContexts
    pub fn diff(&self, other: &Self) -> SymbolicDiff {
        let mut added = HashMap::new();
        let mut removed = HashMap::new();
        let mut changed = HashMap::new();
        let self_flat = self.flatten();
        let other_flat = other.flatten();
        for (k, v) in &other_flat {
            if !self_flat.contains_key(k) {
                added.insert(k.clone(), v.clone());
            } else if self_flat[k] != *v {
                changed.insert(k.clone(), (self_flat[k].clone(), v.clone()));
            }
        }
        for (k, v) in &self_flat {
            if !other_flat.contains_key(k) {
                removed.insert(k.clone(), v.clone());
            }
        }
        SymbolicDiff {
            added,
            removed,
            changed,
        }
    }

    /// Serializes the context (including children) to JSON
    pub fn to_json(&self) -> String {
        serde_json::to_string(&self.flatten()).unwrap()
    }

    /// Loads a context from a flat JSON map
    pub fn from_json(json: &str) -> Self {
        let flat: HashMap<String, String> = serde_json::from_str(json).unwrap();
        let mut ctx = SymbolicContext::new();
        for (k, v) in flat {
            ctx.set(&k, &v);
        }
        ctx
    }

    /// Resolves a path and deserializes the value as type T
    pub fn resolve_as_type<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
        self.resolve_path(path)
            .and_then(|s| serde_json::from_str(&s).ok())
    }

    /// Interpolates variables like "${goal_1.location}" in a string
    pub fn interpolate(&self, input: &str) -> String {
        let mut out = String::new();
        let mut chars = input.chars().peekable();
        while let Some(c) = chars.next() {
            if c == '$' && chars.peek() == Some(&'{') {
                chars.next(); // skip '{'
                let mut var = String::new();
                while let Some(&nc) = chars.peek() {
                    if nc == '}' {
                        chars.next();
                        break;
                    }
                    var.push(nc);
                    chars.next();
                }
                if let Some(val) = self.resolve_path(&var) {
                    out.push_str(&val);
                } else {
                    out.push_str(&format!("${{{}}}", var));
                }
            } else {
                out.push(c);
            }
        }
        out
    }

    /// Returns the resolved value or a default if not found
    pub fn resolve_or_default(&self, path: &str, default: &str) -> String {
        self.resolve_path(path)
            .unwrap_or_else(|| default.to_string())
    }

    /// Optional: snapshot current memory state
    pub fn snapshot(&self) -> HashMap<String, String> {
        self.memory.clone()
    }

    /// Optional: restore memory from a snapshot
    pub fn restore(&mut self, snapshot: HashMap<String, String>) {
        self.memory = snapshot;
    }

    /// Exports the context and its children as a symbolic IR (intermediate representation)
    pub fn to_symbolic_ir(&self) -> Value {
        // Recursively export as a JSON value tree with meta
        let mut obj = serde_json::Map::new();
        for (k, v) in &self.memory {
            obj.insert(k.clone(), Value::String(v.clone()));
        }
        for (k, child) in &self.children {
            obj.insert(k.clone(), child.to_symbolic_ir());
        }
        // Optionally, add meta as a special field
        Value::Object(obj)
    }
}