lean_ctx/core/patterns/
json_schema.rs1pub fn compress(output: &str) -> Option<String> {
2 let trimmed = output.trim();
3
4 if !trimmed.starts_with('{') && !trimmed.starts_with('[') {
5 return None;
6 }
7
8 let val: serde_json::Value = match serde_json::from_str(trimmed) {
9 Ok(v) => v,
10 Err(_) => return None,
11 };
12
13 let schema = extract_schema(&val, 0);
14 Some(schema)
15}
16
17fn extract_schema(val: &serde_json::Value, depth: usize) -> String {
18 let indent = " ".repeat(depth);
19 match val {
20 serde_json::Value::Object(map) => {
21 if map.is_empty() {
22 return format!("{indent}{{}}");
23 }
24 if depth > 3 {
25 return format!("{indent}{{...{} keys}}", map.len());
26 }
27
28 let mut entries = Vec::new();
29 for (key, value) in map.iter().take(20) {
30 let type_str = type_of(value);
31 match value {
32 serde_json::Value::Object(inner) if !inner.is_empty() && depth < 3 => {
33 let nested = extract_schema(value, depth + 1);
34 entries.push(format!("{indent} {key}: {{\n{nested}\n{indent} }}"));
35 }
36 serde_json::Value::Array(arr) if !arr.is_empty() => {
37 let item_type = if let Some(first) = arr.first() {
38 type_of(first)
39 } else {
40 "any".to_string()
41 };
42 entries.push(format!("{indent} {key}: [{item_type}...{}]", arr.len()));
43 }
44 _ => {
45 entries.push(format!("{indent} {key}: {type_str}"));
46 }
47 }
48 }
49 if map.len() > 20 {
50 entries.push(format!("{indent} ...+{} more keys", map.len() - 20));
51 }
52 entries.join("\n")
53 }
54 serde_json::Value::Array(arr) => {
55 if arr.is_empty() {
56 return format!("{indent}[]");
57 }
58 let first_schema = extract_schema(&arr[0], depth + 1);
59 format!(
60 "{indent}[{} items, each:\n{first_schema}\n{indent}]",
61 arr.len()
62 )
63 }
64 other => format!("{indent}{}", type_of(other)),
65 }
66}
67
68fn type_of(val: &serde_json::Value) -> String {
69 match val {
70 serde_json::Value::Null => "null".to_string(),
71 serde_json::Value::Bool(_) => "bool".to_string(),
72 serde_json::Value::Number(n) => {
73 if n.is_f64() {
74 "float".to_string()
75 } else {
76 "int".to_string()
77 }
78 }
79 serde_json::Value::String(s) => {
80 if s.len() > 50 {
81 format!("str({})", s.len())
82 } else {
83 "str".to_string()
84 }
85 }
86 serde_json::Value::Array(arr) => format!("[...{}]", arr.len()),
87 serde_json::Value::Object(map) => format!("{{...{} keys}}", map.len()),
88 }
89}