modulink_rs/context/
mod.rs

1//! Context type for modulink-rust
2//! Serializable, type-safe map for passing data between links.
3//!
4//! # Shadowing Policy (Ergonomic APIs)
5//! For ergonomic usage (`Context`), always use variable shadowing (e.g., `let ctx = ctx.insert(...)`) instead of `mut`.
6//! This prevents accidental mutation and is safer for async/concurrent code. See migration plan for details.
7//!
8//! Example (ergonomic, shadowing):
9//! ```rust
10//! use modulink_rs::context::Context;
11//! let ctx = Context::new();
12//! let ctx = ctx.insert("a", 1);
13//! let ctx = ctx.insert("b", 2);
14//! ```
15//!
16//! Advanced/generic APIs (ContextMutable) may use `mut` for performance, but must document the tradeoff.
17
18use serde::{Deserialize, Serialize};
19use serde_json::Value;
20use std::collections::HashMap;
21
22#[derive(Debug, Clone, Serialize, Deserialize, Default)]
23pub struct Context(pub HashMap<String, Value>);
24
25impl Context {
26    pub fn new() -> Self {
27        Context(HashMap::new())
28    }
29    // Immutable insert: returns a new Context with the value inserted
30    pub fn insert<K: Into<String>, V: Serialize>(self, key: K, value: V) -> Self {
31        let mut new_ctx = self;
32        new_ctx.0.insert(key.into(), serde_json::to_value(value).unwrap());
33        new_ctx
34    }
35    pub fn get<V: for<'de> Deserialize<'de>>(&self, key: &str) -> Option<V> {
36        self.0.get(key).and_then(|v| serde_json::from_value(v.clone()).ok())
37    }
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, Default)]
41pub struct ContextMutable(pub HashMap<String, Value>);
42
43impl ContextMutable {
44    pub fn new() -> Self {
45        ContextMutable(HashMap::new())
46    }
47    // Mutable insert: mutates in place
48    pub fn insert<K: Into<String>, V: Serialize>(&mut self, key: K, value: V) {
49        self.0.insert(key.into(), serde_json::to_value(value).unwrap());
50    }
51    pub fn get<V: for<'de> Deserialize<'de>>(&self, key: &str) -> Option<V> {
52        self.0.get(key).and_then(|v| serde_json::from_value(v.clone()).ok())
53    }
54}