use crate::models::field_names;
use serde_json::Value;
use std::fmt::Write;
pub const FORMAT_TOON_COMPACT: &str = "toon_compact";
pub const FORMAT_JSON: &str = "json";
pub const FORMAT_TOON: &str = "toon";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WireFormat {
#[default]
Json,
Toon,
ToonCompact,
}
#[must_use]
pub fn invalid_format_msg(got: &str) -> String {
format!(
"invalid format '{got}': expected one of {FORMAT_JSON}, {FORMAT_TOON}, {FORMAT_TOON_COMPACT}"
)
}
impl WireFormat {
pub fn parse_http(raw: Option<&str>) -> Result<Self, String> {
match raw {
None => Ok(Self::Json),
Some(s) if s == FORMAT_JSON => Ok(Self::Json),
Some(s) if s == FORMAT_TOON => Ok(Self::Toon),
Some(s) if s == FORMAT_TOON_COMPACT => Ok(Self::ToonCompact),
Some(other) => Err(invalid_format_msg(other)),
}
}
}
const MEMORY_FIELDS: &[&str] = &[
"id",
"title",
"tier",
"namespace",
"priority",
field_names::CONFIDENCE,
"score",
field_names::ACCESS_COUNT,
"tags",
"source",
field_names::CREATED_AT,
field_names::UPDATED_AT,
"metadata",
];
const MEMORY_FIELDS_COMPACT: &[&str] = &[
"id",
"title",
"tier",
"namespace",
"priority",
"score",
"tags",
"agent_id",
];
pub fn memories_to_toon(response: &Value, compact: bool) -> String {
let fields = if compact {
MEMORY_FIELDS_COMPACT
} else {
MEMORY_FIELDS
};
let mut out = String::with_capacity(1024);
let mut meta = Vec::new();
if let Some(count) = response.get("count") {
meta.push(format!("count:{count}"));
}
if let Some(mode) = response.get("mode").and_then(|v| v.as_str()) {
meta.push(format!("mode:{mode}"));
}
if let Some(used) = response.get(field_names::TOKENS_USED) {
meta.push(format!("tokens_used:{used}"));
}
if let Some(budget) = response.get(field_names::BUDGET_TOKENS) {
meta.push(format!("budget_tokens:{budget}"));
}
if !meta.is_empty() {
out.push_str(&meta.join("|"));
out.push('\n');
}
let mut std_list: Vec<&Value> = Vec::new();
if let Some(standard) = response.get("standard") {
std_list.push(standard);
}
if let Some(standards) = response.get("standards").and_then(|v| v.as_array()) {
std_list.extend(standards.iter());
}
if !std_list.is_empty() {
out.push_str("standards[id|title|content]:\n");
for standard in &std_list {
let id = format_value(standard.get("id"));
let title = format_value(standard.get("title"));
let content = format_value(standard.get("content"));
let _ = writeln!(out, "{id}|{title}|{content}");
}
}
out.push_str("memories[");
out.push_str(&fields.join("|"));
out.push_str("]:\n");
if let Some(memories) = response.get("memories").and_then(|v| v.as_array()) {
for mem in memories {
let row: Vec<String> = fields
.iter()
.map(|&field| {
if field == "agent_id" {
format_value(mem.get("metadata").and_then(|m| m.get("agent_id")))
} else {
format_value(mem.get(field))
}
})
.collect();
out.push_str(&row.join("|"));
out.push('\n');
}
}
out
}
pub fn search_to_toon(response: &Value, compact: bool) -> String {
if response.get("results").is_some() && response.get("memories").is_none() {
let mut normalized = response.clone();
if let Some(results) = response.get("results") {
normalized["memories"] = results.clone();
}
return memories_to_toon(&normalized, compact);
}
memories_to_toon(response, compact)
}
fn format_value(val: Option<&Value>) -> String {
match val {
None | Some(Value::Null) => String::new(),
Some(Value::String(s)) => escape_toon(s),
Some(Value::Number(n)) => n.to_string(),
Some(Value::Bool(b)) => {
if *b {
"1".to_string()
} else {
"0".to_string()
}
}
Some(Value::Array(arr)) => {
let items: Vec<String> = arr
.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect();
escape_toon(&items.join(","))
}
Some(obj @ Value::Object(m)) => {
if m.is_empty() {
String::new()
} else {
escape_toon(&serde_json::to_string(obj).unwrap_or_default())
}
}
}
}
fn escape_toon(s: &str) -> String {
if s.contains('|')
|| s.contains('\n')
|| s.contains('\r')
|| s.contains('\\')
|| s.contains(':')
{
s.replace('\\', "\\\\")
.replace('|', "\\|")
.replace(':', "\\:")
.replace('\n', "\\n")
.replace('\r', "\\r")
} else {
s.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Tier;
use serde_json::json;
#[test]
fn issue_1579_b4_wire_format_parse_http() {
assert_eq!(WireFormat::parse_http(None), Ok(WireFormat::Json));
assert_eq!(
WireFormat::parse_http(Some(FORMAT_JSON)),
Ok(WireFormat::Json)
);
assert_eq!(
WireFormat::parse_http(Some(FORMAT_TOON)),
Ok(WireFormat::Toon)
);
assert_eq!(
WireFormat::parse_http(Some(FORMAT_TOON_COMPACT)),
Ok(WireFormat::ToonCompact)
);
}
#[test]
fn issue_1579_b4_wire_format_rejects_unknown_with_ssot_message() {
let err = WireFormat::parse_http(Some("yaml")).unwrap_err();
assert_eq!(err, invalid_format_msg("yaml"));
assert!(err.contains("json") && err.contains("toon") && err.contains("toon_compact"));
assert!(WireFormat::parse_http(Some("TOON")).is_err());
}
#[test]
fn empty_memories() {
let resp = json!({"memories": [], "count": 0, "mode": "keyword"});
let toon = memories_to_toon(&resp, false);
assert!(toon.contains("count:0"));
assert!(toon.contains("mode:keyword"));
assert!(toon.contains("memories["));
let lines: Vec<&str> = toon.lines().collect();
assert_eq!(lines.len(), 2); }
#[test]
fn single_memory() {
let resp = json!({
"memories": [{
"id": "abc-123",
"title": "PostgreSQL config",
"tier": Tier::Long.as_str(),
"namespace": "infra",
"priority": 9,
"confidence": 1.0,
"score": 0.763,
"access_count": 2,
"tags": ["postgres", "database"],
"source": "claude",
"created_at": "2026-04-03T15:00:00+00:00",
"updated_at": "2026-04-03T15:00:00+00:00"
}],
"count": 1,
"mode": "hybrid"
});
let toon = memories_to_toon(&resp, false);
let lines: Vec<&str> = toon.lines().collect();
assert_eq!(lines.len(), 3); assert!(
lines[2].starts_with("abc-123|PostgreSQL config|long|infra|9|"),
"got: {}",
lines[2]
);
assert!(lines[2].contains("postgres,database"));
assert!(lines[2].contains("claude"));
}
#[test]
fn compact_mode_fewer_fields() {
let resp = json!({
"memories": [{"id": "x", "title": "Test", "tier": Tier::Mid.as_str(), "namespace": "test", "priority": 5, "score": 0.5, "tags": []}],
"count": 1
});
let toon = memories_to_toon(&resp, true);
assert!(toon.contains("memories[id|title|tier|namespace|priority|score|tags|agent_id]:"));
assert!(!toon.contains("created_at"));
assert!(!toon.contains("confidence"));
}
#[test]
fn compact_mode_surfaces_agent_id_from_metadata() {
let resp = json!({
"memories": [{
"id": "x",
"title": "Test",
"tier": Tier::Mid.as_str(),
"namespace": "test",
"priority": 5,
"score": 0.5,
"tags": [],
"metadata": {"agent_id": "alice"}
}],
"count": 1
});
let toon = memories_to_toon(&resp, true);
let row = toon.lines().last().unwrap();
assert!(
row.ends_with("|alice"),
"agent_id must be the last compact column; row: {row}"
);
}
#[test]
fn pipe_in_title_escaped() {
let resp = json!({"memories": [{"id": "x", "title": "A|B", "tier": Tier::Mid.as_str()}], "count": 1});
let toon = memories_to_toon(&resp, true);
assert!(toon.contains("A\\|B"));
}
#[test]
fn multiple_memories_token_savings() {
let resp = json!({
"memories": [
{"id": "a", "title": "Memory 1", "tier": Tier::Long.as_str(), "namespace": "test", "priority": 9, "score": 0.9, "tags": ["t1"]},
{"id": "b", "title": "Memory 2", "tier": Tier::Mid.as_str(), "namespace": "test", "priority": 7, "score": 0.7, "tags": ["t2"]},
{"id": "c", "title": "Memory 3", "tier": Tier::Short.as_str(), "namespace": "test", "priority": 5, "score": 0.5, "tags": ["t3"]}
],
"count": 3,
"mode": "hybrid"
});
let toon = memories_to_toon(&resp, true);
let json_str = serde_json::to_string(&resp).unwrap();
assert!(
toon.len() < json_str.len(),
"TOON ({}) should be shorter than JSON ({})",
toon.len(),
json_str.len()
);
}
#[test]
fn search_results_key() {
let resp = json!({"results": [{"id": "x", "title": "Found", "tier": Tier::Mid.as_str()}], "count": 1});
let toon = search_to_toon(&resp, true);
assert!(toon.contains("memories["));
assert!(toon.contains("Found"));
}
fn five_memory_fixture() -> Value {
json!({
"memories": [
{
"id": "01",
"title": "PostgreSQL config",
"tier": Tier::Long.as_str(),
"namespace": "infra",
"priority": 9,
"confidence": 1.0,
"score": 0.91,
"access_count": 4,
"tags": ["postgres", "database"],
"source": "claude",
"created_at": "2026-04-03T15:00:00+00:00",
"updated_at": "2026-04-03T15:00:00+00:00",
"metadata": {"agent_id": "alice"}
},
{
"id": "02",
"title": "Redis cache strategy",
"tier": Tier::Long.as_str(),
"namespace": "infra",
"priority": 8,
"confidence": 0.95,
"score": 0.84,
"access_count": 2,
"tags": ["redis", "cache"],
"source": "claude",
"created_at": "2026-04-03T15:01:00+00:00",
"updated_at": "2026-04-03T15:01:00+00:00",
"metadata": {"agent_id": "alice"}
},
{
"id": "03",
"title": "BIND9 custom build",
"tier": Tier::Mid.as_str(),
"namespace": "infra/dns",
"priority": 7,
"confidence": 0.9,
"score": 0.71,
"access_count": 1,
"tags": ["bind", "dns"],
"source": "user",
"created_at": "2026-04-03T15:02:00+00:00",
"updated_at": "2026-04-03T15:02:00+00:00",
"metadata": {"agent_id": "bob"}
},
{
"id": "04",
"title": "Kubernetes pod recovery",
"tier": Tier::Mid.as_str(),
"namespace": "platform/k8s",
"priority": 6,
"confidence": 0.85,
"score": 0.62,
"access_count": 0,
"tags": ["k8s", "ops"],
"source": "hook",
"created_at": "2026-04-03T15:03:00+00:00",
"updated_at": "2026-04-03T15:03:00+00:00",
"metadata": {"agent_id": "carol"}
},
{
"id": "05",
"title": "Vault secrets rotation",
"tier": Tier::Short.as_str(),
"namespace": "security",
"priority": 5,
"confidence": 0.8,
"score": 0.55,
"access_count": 3,
"tags": ["vault", "secrets"],
"source": "api",
"created_at": "2026-04-03T15:04:00+00:00",
"updated_at": "2026-04-03T15:04:00+00:00",
"metadata": {"agent_id": "dave"}
}
],
"count": 5,
"mode": "hybrid"
})
}
#[test]
fn test_toon_size_invariant_5_memories_under_threshold() {
let fixture = five_memory_fixture();
let json_bytes = serde_json::to_string(&fixture).unwrap().len();
let toon_bytes = memories_to_toon(&fixture, true).len();
let ratio = (toon_bytes as f64) / (json_bytes as f64);
assert!(
ratio < 0.65,
"TOON size invariant violated: toon={toon_bytes} json={json_bytes} \
ratio={ratio:.3} (must be < 0.65 for 5-memory compact fixture)"
);
let toon = memories_to_toon(&fixture, true);
for id in ["01", "02", "03", "04", "05"] {
assert!(toon.contains(id), "TOON output missing id `{id}`");
}
}
#[test]
fn escape_toon_pipe() {
let s = escape_toon("a|b");
assert_eq!(s, "a\\|b");
}
#[test]
fn escape_toon_newline() {
let s = escape_toon("a\nb");
assert_eq!(s, "a\\nb");
}
#[test]
fn escape_toon_carriage_return() {
let s = escape_toon("a\rb");
assert_eq!(s, "a\\rb");
}
#[test]
fn escape_toon_backslash() {
let s = escape_toon("a\\b");
assert_eq!(s, "a\\\\b");
}
#[test]
fn escape_toon_colon() {
let s = escape_toon("a:b");
assert_eq!(s, "a\\:b");
}
#[test]
fn escape_toon_no_special_chars_passthrough() {
let s = escape_toon("plain text 123");
assert_eq!(s, "plain text 123");
}
#[test]
fn escape_toon_multiple_specials() {
let s = escape_toon("a|b:c\nd");
assert!(s.contains("\\|"));
assert!(s.contains("\\:"));
assert!(s.contains("\\n"));
}
#[test]
fn format_value_null_is_empty() {
let resp = json!({
"memories": [{"id": null, "title": "t"}],
"count": 1,
});
let toon = memories_to_toon(&resp, true);
let row = toon.lines().last().unwrap();
assert!(row.starts_with("|t|"), "got: {row}");
}
#[test]
fn format_value_bool_serializes_as_zero_one() {
let resp = json!({
"memories": [{"id": "x", "title": true}],
"count": 1,
});
let toon = memories_to_toon(&resp, true);
let row = toon.lines().last().unwrap();
assert!(row.contains("|1|"), "true → 1; got: {row}");
}
#[test]
fn format_value_bool_false() {
let resp = json!({
"memories": [{"id": "x", "title": false}],
"count": 1,
});
let toon = memories_to_toon(&resp, true);
let row = toon.lines().last().unwrap();
assert!(row.contains("|0|"), "false → 0; got: {row}");
}
#[test]
fn format_value_object_empty_is_empty_string() {
let resp = json!({
"memories": [{
"id": "x", "title": "t", "tier": Tier::Long.as_str(), "namespace": "n",
"priority": 1, "confidence": 1.0, "score": 0.5, "access_count": 0,
"tags": [], "source": "", "created_at": "", "updated_at": "",
"metadata": {}
}],
"count": 1,
});
let toon = memories_to_toon(&resp, false);
let row = toon.lines().last().unwrap();
assert!(row.ends_with('|') || row.ends_with("||"), "got: {row}");
}
#[test]
fn format_value_object_non_empty_serialized_json() {
let resp = json!({
"memories": [{
"id": "x", "title": "t", "tier": Tier::Long.as_str(), "namespace": "n",
"priority": 1, "confidence": 1.0, "score": 0.5, "access_count": 0,
"tags": [], "source": "", "created_at": "", "updated_at": "",
"metadata": {"k": "v"}
}],
"count": 1,
});
let toon = memories_to_toon(&resp, false);
assert!(toon.contains("k") && toon.contains("v"));
}
#[test]
fn standards_section_emitted_when_present() {
let resp = json!({
"memories": [],
"count": 0,
"standard": {"id": "s1", "title": "policy", "content": "be nice"}
});
let toon = memories_to_toon(&resp, true);
assert!(toon.contains("standards[id|title|content]:"));
assert!(toon.contains("s1"));
}
#[test]
fn standards_array_emitted_when_present() {
let resp = json!({
"memories": [],
"count": 0,
"standards": [
{"id": "s1", "title": "p1", "content": "c1"},
{"id": "s2", "title": "p2", "content": "c2"},
],
});
let toon = memories_to_toon(&resp, true);
assert!(toon.contains("standards["));
assert!(toon.contains("s1"));
assert!(toon.contains("s2"));
}
#[test]
fn meta_line_includes_token_budget() {
let resp = json!({
"memories": [],
"count": 0,
"tokens_used": 100,
"budget_tokens": 500,
});
let toon = memories_to_toon(&resp, true);
assert!(toon.contains("tokens_used:100"));
assert!(toon.contains("budget_tokens:500"));
}
#[test]
fn search_to_toon_passes_through_when_memories_present() {
let resp = json!({
"memories": [{"id": "a", "title": "t1"}],
"results": [{"id": "b", "title": "t2"}],
"count": 1,
});
let toon = search_to_toon(&resp, true);
assert!(toon.contains("a"));
assert!(toon.contains("t1"));
}
#[test]
fn test_toon_round_trip_preserves_visible_fields() {
let resp = json!({
"memories": [{
"id": "abc-xyz",
"title": "Round-trip test",
"tier": Tier::Long.as_str(),
"namespace": "test",
"priority": 9,
"confidence": 1.0,
"score": 0.5,
"access_count": 7,
"tags": ["alpha", "beta"],
"source": "claude",
"created_at": "2026-04-03T15:00:00+00:00",
"updated_at": "2026-04-03T15:00:30+00:00",
"metadata": {"agent_id": "alice"}
}],
"count": 1
});
let toon = memories_to_toon(&resp, false);
for col in [
"id",
"title",
"tier",
"namespace",
"priority",
"confidence",
"score",
"access_count",
"tags",
"source",
"created_at",
"updated_at",
"metadata",
] {
assert!(
toon.contains(col),
"TOON header must list column `{col}`; got:\n{toon}"
);
}
assert!(toon.contains("abc-xyz"));
assert!(toon.contains("Round-trip test"));
assert!(toon.contains("alpha,beta")); assert!(
toon.contains(r"2026-04-03T15\:00\:00+00\:00"),
"TOON should contain timestamp (with escaped ':'): {toon}"
);
}
}