juglans 0.1.0

Compiler and runtime for Juglans Workflow Language (JWL)
Documentation
// src/core/context.rs
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use serde_json::{Value, json};
use anyhow::{Result, anyhow};

/// A thread-safe, shared state for a single workflow execution.
#[derive(Debug, Clone)]
pub struct WorkflowContext {
    data: Arc<Mutex<Value>>,
}

impl WorkflowContext {
    /// Creates a new, empty context, initialized as a JSON object.
    pub fn new() -> Self {
        Self {
            data: Arc::new(Mutex::new(json!({}))),
        }
    }

    /// 【修改】Sets a value in the context using a dot-notation path.
    /// This will create nested objects as needed.
    pub fn set(&self, path: String, value: Value) -> Result<()> {
        let mut data = self.data.lock()
            .map_err(|_| anyhow!("Failed to acquire context lock"))?;

        let parts: Vec<&str> = path.split('.').collect();
        let (last_key, parent_parts) = parts.split_last()
            .ok_or_else(|| anyhow!("Cannot set a value with an empty path"))?;

        let mut current = &mut *data;

        // Traverse or create nested objects for the parent path
        for part in parent_parts {
            current = current.as_object_mut()
                .ok_or_else(|| anyhow!(format!("Path part '{}' is not an object", part)))?
                .entry(part.to_string())
                .or_insert_with(|| json!({}));
        }

        // Set the final value
        if let Some(obj) = current.as_object_mut() {
            obj.insert(last_key.to_string(), value);
        } else {
            return Err(anyhow!("Final path segment is not an object"));
        }

        Ok(())
    }

    /// Resolves a dot-notation path to a value in the context.
    pub fn resolve_path(&self, path: &str) -> Result<Option<Value>> {
        let parts: Vec<&str> = path.split('.').collect();
        if parts.is_empty() {
            return Ok(None);
        }

        let data = self.data.lock()
            .map_err(|_| anyhow!("Failed to acquire context lock"))?;
        
        // Use serde_json's built-in pointer, which is more robust
        let pointer = format!("/{}", parts.join("/"));
        Ok(data.pointer(&pointer).cloned())
    }

    /// Returns a snapshot of the context as a serde_json::Value.
    pub fn get_as_value(&self) -> Result<Value> {
        let data = self.data.lock()
            .map_err(|_| anyhow!("Failed to acquire context lock"))?;
        Ok(data.clone())
    }
}