use std::collections::HashMap;
use serde_json::Value;
const TEXT_HEAD_BYTES: usize = 400;
const TEXT_TAIL_BYTES: usize = 200;
const SEQ_HEAD_ITEMS: usize = 3;
const SEQ_TAIL_ITEMS: usize = 2;
const ELEMENT_BUDGET: usize = 120;
#[derive(Default, Debug)]
pub struct SessionValues {
values: HashMap<String, Value>,
next: usize,
}
impl SessionValues {
pub fn new() -> Self {
Self::default()
}
pub fn put(&mut self, value: Value) -> String {
self.next += 1;
let handle = format!("r{}", self.next);
self.values.insert(handle.clone(), value);
handle
}
pub fn get(&self, handle: &str) -> Option<&Value> {
self.values.get(handle)
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn resolve_refs(&self, params: &mut Value) -> Vec<String> {
let mut resolved = Vec::new();
self.resolve_into(params, &mut resolved);
resolved
}
fn lookup(&self, reference: &str) -> Option<&Value> {
let mut parts = reference.split('.');
let mut cursor = self.get(parts.next()?)?;
for part in parts {
cursor = match cursor {
Value::Object(map) => map.get(part)?,
Value::Array(items) => items.get(part.parse::<usize>().ok()?)?,
_ => return None,
};
}
Some(cursor)
}
fn resolve_into(&self, node: &mut Value, resolved: &mut Vec<String>) {
match node {
Value::String(s) => {
if let Some(reference) = s.strip_prefix('$') {
if let Some(value) = self.lookup(reference) {
resolved.push(reference.to_string());
*node = value.clone();
}
}
}
Value::Array(items) => {
for item in items {
self.resolve_into(item, resolved);
}
}
Value::Object(map) => {
for (_, v) in map.iter_mut() {
self.resolve_into(v, resolved);
}
}
_ => {}
}
}
}
pub fn render_preview(handle: &str, value: &Value) -> String {
match value {
Value::Array(items) => render_array(handle, items),
Value::Object(map) => render_object(handle, map),
Value::String(s) => render_text(handle, s),
other => format!("{handle} = {other}"),
}
}
fn render_array(handle: &str, items: &[Value]) -> String {
let len = items.len();
if len <= SEQ_HEAD_ITEMS + SEQ_TAIL_ITEMS {
let all: Vec<String> = items.iter().map(summarize_element).collect();
return format!("{handle} = array(len={len}, [{}])", all.join(", "));
}
let head: Vec<String> = items
.iter()
.take(SEQ_HEAD_ITEMS)
.map(summarize_element)
.collect();
let tail: Vec<String> = items[len - SEQ_TAIL_ITEMS..]
.iter()
.map(summarize_element)
.collect();
format!(
"{handle} = array(len={len}, [:{}]=[{}], [-{}:]=[{}])",
SEQ_HEAD_ITEMS,
head.join(", "),
SEQ_TAIL_ITEMS,
tail.join(", ")
)
}
fn render_object(handle: &str, map: &serde_json::Map<String, Value>) -> String {
let bytes = Value::Object(map.clone()).to_string().len();
if map.len() > 24 {
let keys: Vec<&str> = map.keys().take(8).map(String::as_str).collect();
return format!(
"{handle} = json(keys={} total, first=[{}, …], bytes={bytes})",
map.len(),
keys.join(", ")
);
}
let fields: Vec<String> = map
.iter()
.map(|(k, v)| format!("{k}: {}", describe_field(v)))
.collect();
format!("{handle} = json({}, bytes={bytes})", fields.join(", "))
}
fn describe_field(value: &Value) -> String {
match value {
Value::String(s) if s.len() > ELEMENT_BUDGET => {
format!("text(len={}, lines={})", human(s.len()), s.lines().count())
}
Value::String(s) => format!("{s:?}"),
Value::Array(items) => format!("array(len={})", items.len()),
Value::Object(inner) => format!("json(keys={})", inner.len()),
other => other.to_string(),
}
}
fn render_text(handle: &str, s: &str) -> String {
let bytes = s.len();
let lines = s.lines().count();
if bytes <= TEXT_HEAD_BYTES + TEXT_TAIL_BYTES {
return format!("{handle} = text(len={}, lines={lines}) {s:?}", human(bytes));
}
let head = clip(s, TEXT_HEAD_BYTES, true);
let tail = clip(s, TEXT_TAIL_BYTES, false);
format!(
"{handle} = text(len={}, lines={lines}, head={head:?}, tail={tail:?})",
human(bytes)
)
}
fn summarize_element(value: &Value) -> String {
match value {
Value::Object(map) => {
let inner: Vec<String> = map
.iter()
.take(2)
.map(|(k, v)| format!("{k}:{}", clip_str(&scalar_or_type(v), 24)))
.collect();
let more = if map.len() > 2 { ", …" } else { "" };
format!("{{{}{more}}}", inner.join(","))
}
Value::Array(items) => format!("array(len={})", items.len()),
Value::String(s) => format!("{:?}", clip_str(s, ELEMENT_BUDGET)),
other => clip_str(&other.to_string(), ELEMENT_BUDGET),
}
}
fn scalar_or_type(value: &Value) -> String {
match value {
Value::Object(_) => "{…}".to_string(),
Value::Array(items) => format!("array({})", items.len()),
Value::String(s) => s.clone(),
other => other.to_string(),
}
}
pub(crate) fn clip_str(s: &str, budget: usize) -> String {
if s.len() <= budget {
return s.to_string();
}
let mut end = budget;
while !s.is_char_boundary(end) {
end -= 1;
}
format!("{}…", &s[..end])
}
fn clip(s: &str, budget: usize, from_head: bool) -> String {
if s.len() <= budget {
return s.to_string();
}
if from_head {
let mut end = budget;
while !s.is_char_boundary(end) {
end -= 1;
}
s[..end].to_string()
} else {
let mut start = s.len() - budget;
while !s.is_char_boundary(start) {
start += 1;
}
s[start..].to_string()
}
}
fn human(bytes: usize) -> String {
const KB: usize = 1024;
const MB: usize = KB * 1024;
if bytes >= MB {
format!("{:.1}MB", bytes as f64 / MB as f64)
} else if bytes >= KB {
format!("{:.1}KB", bytes as f64 / KB as f64)
} else {
format!("{bytes}B")
}
}
pub fn reference_hint(handle: &str) -> String {
format!(
"\n[full value retained this run — pass \"${handle}\" as a tool argument \
to operate on all of it, or \"${handle}.<field>\" for one field]"
)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn handles_are_short_and_sequential() {
let mut store = SessionValues::new();
assert_eq!(store.put(json!(1)), "r1");
assert_eq!(store.put(json!(2)), "r2");
assert_eq!(store.get("r1"), Some(&json!(1)));
assert_eq!(store.get("nope"), None);
assert_eq!(store.len(), 2);
}
#[test]
fn the_elided_data_is_recoverable_through_the_handle() {
let mut store = SessionValues::new();
let rows: Vec<Value> = (0..100).map(|i| json!({"id": i})).collect();
let handle = store.put(json!(rows));
let preview = render_preview(&handle, store.get(&handle).unwrap());
assert!(
preview.contains("len=100"),
"true length must survive: {preview}"
);
let recovered = store.get(&handle).unwrap().as_array().unwrap();
assert_eq!(recovered.len(), 100);
assert_eq!(recovered[57], json!({"id": 57}));
}
#[test]
fn array_preview_carries_type_length_and_both_ends() {
let items: Vec<Value> = (0..100).map(|i| json!(i)).collect();
let p = render_preview("r1", &json!(items));
assert!(p.starts_with("r1 = array(len=100"), "{p}");
assert!(p.contains("[:3]=[0, 1, 2]"), "head sample missing: {p}");
assert!(p.contains("[-2:]=[98, 99]"), "tail sample missing: {p}");
}
#[test]
fn short_arrays_render_whole() {
let p = render_preview("r1", &json!([1, 2, 3]));
assert_eq!(p, "r1 = array(len=3, [1, 2, 3])");
}
#[test]
fn object_preview_describes_fields_not_just_key_names() {
let p = render_preview("r2", &json!({"name": "car", "version": "0.46.1"}));
assert!(
p.contains(r#"name: "car""#),
"small values shown literally: {p}"
);
assert!(p.contains("bytes="), "true size must be stated: {p}");
}
#[test]
fn a_large_field_inside_an_envelope_announces_itself() {
let body = format!("HEAD\n{}\nTAIL", "z".repeat(50_000));
let value = json!({
"content": body,
"path": "./big.txt",
"size_bytes": 50_010,
"total_lines": 3,
});
let p = render_preview("r1", &value);
assert!(
p.contains("content: text(len=") && p.contains("lines=3"),
"the big field must state its size and shape: {p}"
);
assert!(
p.contains(r#"path: "./big.txt""#),
"small fields stay literal: {p}"
);
assert!(
p.len() < 400,
"preview must stay bounded: {} bytes",
p.len()
);
}
#[test]
fn dotted_paths_address_a_field_of_a_retained_value() {
let mut store = SessionValues::new();
let h = store.put(json!({"content": "the payload", "path": "./f.txt"}));
let mut params = json!({"content": format!("${h}.content")});
let resolved = store.resolve_refs(&mut params);
assert_eq!(resolved, vec![format!("{h}.content")]);
assert_eq!(params["content"], json!("the payload"));
let rows = store.put(json!([{"id": 1}, {"id": 2}, {"id": 3}]));
let mut p2 = json!({"row": format!("${rows}.1.id")});
store.resolve_refs(&mut p2);
assert_eq!(p2["row"], json!(2));
let mut p3 = json!({"x": format!("${h}.nope")});
assert!(store.resolve_refs(&mut p3).is_empty());
assert_eq!(p3["x"], json!(format!("${h}.nope")));
}
#[test]
fn text_preview_reports_size_lines_and_both_ends() {
let body = format!("FIRST LINE\n{}\nLAST LINE", "x".repeat(5_000));
let p = render_preview("r3", &json!(body));
assert!(p.contains("text(len="), "{p}");
assert!(p.contains("lines=3"), "line count missing: {p}");
assert!(p.contains("FIRST LINE"), "head missing: {p}");
assert!(p.contains("LAST LINE"), "tail missing: {p}");
assert!(p.len() < 1_200, "preview not bounded: {} bytes", p.len());
}
#[test]
fn multibyte_text_does_not_panic() {
let body = "€".repeat(5_000);
let p = render_preview("r1", &json!(body));
assert!(p.contains("text(len="), "{p}");
}
#[test]
fn whole_string_references_resolve() {
let mut store = SessionValues::new();
let handle = store.put(json!([1, 2, 3]));
let mut params = json!({"rows": format!("${handle}"), "limit": 10});
let resolved = store.resolve_refs(&mut params);
assert_eq!(resolved, vec![handle]);
assert_eq!(params["rows"], json!([1, 2, 3]));
assert_eq!(params["limit"], json!(10));
}
#[test]
fn nested_references_resolve() {
let mut store = SessionValues::new();
let h = store.put(json!("hello"));
let mut params = json!({"outer": {"inner": [format!("${h}")]}});
store.resolve_refs(&mut params);
assert_eq!(params["outer"]["inner"][0], json!("hello"));
}
#[test]
fn partial_matches_are_left_alone() {
let mut store = SessionValues::new();
let h = store.put(json!("VALUE"));
let mut params = json!({"cmd": format!("echo ${h} > out.txt"), "re": "^\\$r1$"});
let resolved = store.resolve_refs(&mut params);
assert!(
resolved.is_empty(),
"a substring must not trigger substitution: {params}"
);
assert_eq!(params["cmd"], json!(format!("echo ${h} > out.txt")));
}
#[test]
fn unknown_handles_are_left_untouched() {
let store = SessionValues::new();
let mut params = json!({"rows": "$r99"});
let resolved = store.resolve_refs(&mut params);
assert!(resolved.is_empty());
assert_eq!(params["rows"], json!("$r99"));
}
#[test]
fn reference_hint_names_the_typed_form() {
let hint = reference_hint("r7");
assert!(
hint.contains("\"$r7\""),
"the model must see the exact form: {hint}"
);
}
}