1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct JsonRpcRequest {
9 pub jsonrpc: String,
10 pub method: String,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub params: Option<Value>,
13 pub id: Value,
14}
15
16impl JsonRpcRequest {
17 pub fn new(method: &str, params: Option<Value>, id: u64) -> Self {
19 Self {
20 jsonrpc: "2.0".to_string(),
21 method: method.to_string(),
22 params,
23 id: Value::Number(id.into()),
24 }
25 }
26}
27
28pub fn encode_json_rpc(method: &str, params: &Value, id: u64) -> String {
30 let request = JsonRpcRequest {
31 jsonrpc: "2.0".to_string(),
32 method: method.to_string(),
33 params: Some(params.clone()),
34 id: Value::Number(id.into()),
35 };
36 serde_json::to_string(&request).unwrap_or_default()
37}
38
39pub fn measure_json_rpc_size(method: &str, params: &Value, id: u64) -> usize {
41 encode_json_rpc(method, params, id).len()
42}
43
44pub const DCP_HEADER_SIZE: usize = 8;
46
47pub const DCP_INVOCATION_SIZE: usize = 20;
49
50pub fn measure_dcp_size(args_size: usize) -> usize {
52 DCP_HEADER_SIZE + DCP_INVOCATION_SIZE + args_size
54}
55
56#[derive(Debug, Clone)]
58pub struct SizeComparison {
59 pub json_rpc_size: usize,
61 pub dcp_size: usize,
63 pub ratio: f64,
65}
66
67impl SizeComparison {
68 pub fn new(json_rpc_size: usize, dcp_size: usize) -> Self {
70 let ratio = if dcp_size > 0 {
71 json_rpc_size as f64 / dcp_size as f64
72 } else {
73 0.0
74 };
75 Self {
76 json_rpc_size,
77 dcp_size,
78 ratio,
79 }
80 }
81
82 pub fn dcp_is_smaller_by(&self, factor: f64) -> bool {
84 self.ratio >= factor
85 }
86}
87
88pub fn compare_sizes(method: &str, params: &Value, args_binary_size: usize) -> SizeComparison {
90 let json_size = measure_json_rpc_size(method, params, 1);
91 let dcp_size = measure_dcp_size(args_binary_size);
92 SizeComparison::new(json_size, dcp_size)
93}
94
95pub fn estimate_binary_size(value: &Value) -> usize {
97 match value {
98 Value::Null => 0,
99 Value::Bool(_) => 1,
100 Value::Number(n) => {
101 if n.is_i64() || n.is_f64() {
102 8
103 } else {
104 4
105 }
106 }
107 Value::String(s) => 4 + s.len(), Value::Array(arr) => {
109 4 + arr.iter().map(estimate_binary_size).sum::<usize>() }
111 Value::Object(obj) => {
112 4 + obj
113 .iter()
114 .map(|(k, v)| 1 + k.len() + estimate_binary_size(v))
115 .sum::<usize>()
116 }
117 }
118}
119
120pub fn compare_sizes_auto(method: &str, params: &Value) -> SizeComparison {
122 let binary_size = estimate_binary_size(params);
123 compare_sizes(method, params, binary_size)
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use serde_json::json;
130
131 #[test]
132 fn test_json_rpc_encoding() {
133 let params = json!({"name": "test", "value": 42});
134 let encoded = encode_json_rpc("tools/call", ¶ms, 1);
135
136 assert!(encoded.contains("\"jsonrpc\":\"2.0\""));
137 assert!(encoded.contains("\"method\":\"tools/call\""));
138 assert!(encoded.contains("\"params\""));
139 assert!(encoded.contains("\"id\":1"));
140 }
141
142 #[test]
143 fn test_size_comparison() {
144 let params = json!({"name": "test"});
145 let comparison = compare_sizes("test", ¶ms, 8);
146
147 assert!(comparison.json_rpc_size > 0);
148 assert!(comparison.dcp_size > 0);
149 assert!(comparison.ratio > 0.0);
150 }
151
152 #[test]
153 fn test_dcp_smaller_for_simple_call() {
154 let params = json!({"path": "/tmp/test.txt"});
156 let comparison = compare_sizes_auto("read_file", ¶ms);
157
158 println!(
160 "JSON-RPC: {} bytes, DCP: {} bytes, ratio: {:.2}x",
161 comparison.json_rpc_size, comparison.dcp_size, comparison.ratio
162 );
163
164 assert!(comparison.ratio >= 1.4);
166 }
167
168 #[test]
169 fn test_estimate_binary_size() {
170 assert_eq!(estimate_binary_size(&json!(null)), 0);
171 assert_eq!(estimate_binary_size(&json!(true)), 1);
172 assert_eq!(estimate_binary_size(&json!(42)), 8);
173 assert_eq!(estimate_binary_size(&json!(3.14)), 8);
174 assert_eq!(estimate_binary_size(&json!("hello")), 4 + 5);
175 }
176
177 #[test]
178 fn test_size_comparison_ratio() {
179 let comparison = SizeComparison::new(100, 20);
180 assert_eq!(comparison.ratio, 5.0);
181 assert!(comparison.dcp_is_smaller_by(5.0));
182 assert!(!comparison.dcp_is_smaller_by(6.0));
183 }
184}