use crate::types::{RunAgentError, RunAgentResult, SafeMessage};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Clone)]
pub struct CoreSerializer {
max_size_bytes: usize,
}
impl CoreSerializer {
pub fn new(max_size_mb: f64) -> RunAgentResult<Self> {
Ok(Self {
max_size_bytes: (max_size_mb * 1024.0 * 1024.0) as usize,
})
}
pub fn serialize_object(&self, obj: Value) -> RunAgentResult<String> {
let serialized_data = self.try_serialize_strategies(obj)?;
let json_str = serde_json::to_string(&serialized_data)?;
if !self.check_size_limit(&json_str) {
tracing::warn!(
"Serialized object exceeds size limit: {} bytes",
json_str.len()
);
}
Ok(json_str)
}
pub fn prepare_for_deserialization(&self, value: Value) -> Value {
if let Some(str_val) = value.as_str() {
match serde_json::from_str::<Value>(str_val) {
Ok(parsed) => parsed,
Err(_) => value, }
} else {
value }
}
pub fn deserialize_object(&self, json_resp: Value) -> RunAgentResult<Value> {
if let Value::Object(ref map) = json_resp {
if map.contains_key("type") && map.contains_key("payload") {
let payload_val = map.get("payload").unwrap();
if let Some(payload_str) = payload_val.as_str() {
match serde_json::from_str::<Value>(payload_str) {
Ok(parsed) => {
return Ok(parsed);
}
Err(_) => {
return Ok(Value::String(payload_str.to_string()));
}
}
}
return self.reconstruct_nested_json(payload_val.clone());
}
if let Some(content) = map.get("content") {
return self.reconstruct_nested_json(content.clone());
}
}
if let Some(str_val) = json_resp.as_str() {
match serde_json::from_str::<Value>(str_val) {
Ok(parsed) => return self.reconstruct_nested_json(parsed),
Err(_) => return Ok(Value::String(str_val.to_string())),
}
}
self.reconstruct_nested_json(json_resp)
}
pub fn serialize_message(&self, message: &SafeMessage) -> RunAgentResult<String> {
let message_dict = message.to_dict();
let mut serialized_dict = message_dict;
if let Some(data) = serialized_dict.get("data") {
serialized_dict.insert("data".to_string(), self.deep_serialize_value(data.clone())?);
}
if let Some(metadata) = serialized_dict.get("metadata") {
serialized_dict.insert(
"metadata".to_string(),
self.deep_serialize_value(metadata.clone())?,
);
}
let json_str = serde_json::to_string(&serialized_dict)?;
if !self.check_size_limit(&json_str) {
tracing::warn!(
"Serialized message exceeds size limit: {} bytes",
json_str.len()
);
}
Ok(json_str)
}
pub fn deserialize_message(&self, json_str: &str) -> RunAgentResult<SafeMessage> {
let deserialized_data: Value = serde_json::from_str(json_str)?;
let obj = deserialized_data
.as_object()
.ok_or_else(|| RunAgentError::validation("JSON must deserialize to an object"))?;
let mut message_data = obj.clone();
if let Some(data) = message_data.get("data") {
message_data.insert(
"data".to_string(),
self.reconstruct_nested_json(data.clone())?,
);
}
if let Some(metadata) = message_data.get("metadata") {
message_data.insert(
"metadata".to_string(),
self.reconstruct_nested_json(metadata.clone())?,
);
}
let safe_message: SafeMessage = serde_json::from_value(Value::Object(message_data))?;
Ok(safe_message)
}
pub fn check_size_limit(&self, json_str: &str) -> bool {
json_str.len() <= self.max_size_bytes
}
fn try_serialize_strategies(&self, obj: Value) -> RunAgentResult<HashMap<String, Value>> {
if self.is_json_serializable(&obj) {
return Ok(self.create_response("direct", obj));
}
let str_repr = self.value_to_string(&obj);
Ok(self.create_response_with_metadata("string_repr", Value::String(str_repr), &obj))
}
#[allow(clippy::only_used_in_recursion)]
fn is_json_serializable(&self, obj: &Value) -> bool {
match obj {
Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => true,
Value::Array(arr) => arr.iter().all(|item| self.is_json_serializable(item)),
Value::Object(map) => map.values().all(|value| self.is_json_serializable(value)),
}
}
fn value_to_string(&self, obj: &Value) -> String {
match obj {
Value::String(s) => s.clone(),
_ => {
serde_json::to_string(obj).unwrap_or_else(|_| format!("<Unserializable {:?}>", obj))
}
}
}
fn create_response(&self, strategy: &str, content: Value) -> HashMap<String, Value> {
let mut response = HashMap::new();
response.insert("content".to_string(), content);
response.insert("strategy".to_string(), Value::String(strategy.to_string()));
response
}
fn create_response_with_metadata(
&self,
strategy: &str,
content: Value,
original: &Value,
) -> HashMap<String, Value> {
let mut response = self.create_response(strategy, content);
response.insert(
"type".to_string(),
Value::String(self.get_value_type(original)),
);
response.insert("metadata".to_string(), self.extract_metadata(original));
response
}
fn get_value_type(&self, obj: &Value) -> String {
match obj {
Value::Null => "null".to_string(),
Value::Bool(_) => "boolean".to_string(),
Value::Number(_) => "number".to_string(),
Value::String(_) => "string".to_string(),
Value::Array(_) => "array".to_string(),
Value::Object(_) => "object".to_string(),
}
}
fn extract_metadata(&self, obj: &Value) -> Value {
let mut metadata = HashMap::new();
let obj_str = serde_json::to_string(obj).unwrap_or_default();
let obj_size = obj_str.len();
metadata.insert(
"object_type".to_string(),
Value::String(self.get_value_type(obj)),
);
metadata.insert(
"object_size".to_string(),
Value::Number(serde_json::Number::from(obj_size)),
);
metadata.insert("is_null".to_string(), Value::Bool(obj.is_null()));
metadata.insert("is_array".to_string(), Value::Bool(obj.is_array()));
metadata.insert("is_object".to_string(), Value::Bool(obj.is_object()));
Value::Object(metadata.into_iter().collect())
}
#[allow(clippy::only_used_in_recursion)]
fn deep_serialize_value(&self, value: Value) -> RunAgentResult<Value> {
match value {
Value::Object(map) => {
let mut result = serde_json::Map::new();
for (key, val) in map {
result.insert(key, self.deep_serialize_value(val)?);
}
Ok(Value::Object(result))
}
Value::Array(arr) => {
let mut result = Vec::new();
for item in arr {
result.push(self.deep_serialize_value(item)?);
}
Ok(Value::Array(result))
}
_ => Ok(value), }
}
#[allow(clippy::only_used_in_recursion)]
fn reconstruct_nested_json(&self, data: Value) -> RunAgentResult<Value> {
match data {
Value::Object(ref map) => {
if let (Some(strategy), Some(content)) = (
map.get("strategy").and_then(|s| s.as_str()),
map.get("content"),
) {
match strategy {
"direct" => Ok(content.clone()),
"string_repr" => Ok(content.clone()),
_ => Ok(data),
}
} else {
let mut result = serde_json::Map::new();
for (key, value) in map {
result.insert(key.clone(), self.reconstruct_nested_json(value.clone())?);
}
Ok(Value::Object(result))
}
}
Value::Array(arr) => {
let mut result = Vec::new();
for item in arr {
result.push(self.reconstruct_nested_json(item)?);
}
Ok(Value::Array(result))
}
_ => Ok(data),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{MessageType, SafeMessage};
#[test]
fn test_serializer_creation() {
let serializer = CoreSerializer::new(5.0).unwrap();
assert_eq!(serializer.max_size_bytes, 5 * 1024 * 1024);
}
#[test]
fn test_object_serialization() {
let serializer = CoreSerializer::new(10.0).unwrap();
let obj = serde_json::json!({"key": "value", "number": 42});
let result = serializer.serialize_object(obj);
assert!(result.is_ok());
}
#[test]
fn test_message_serialization() {
let serializer = CoreSerializer::new(10.0).unwrap();
let message = SafeMessage::new(
"test-id".to_string(),
MessageType::Status,
serde_json::json!({"status": "ok"}),
);
let result = serializer.serialize_message(&message);
assert!(result.is_ok());
let serialized = result.unwrap();
let deserialized = serializer.deserialize_message(&serialized);
assert!(deserialized.is_ok());
}
#[test]
fn test_size_limit_check() {
let serializer = CoreSerializer::new(0.001).unwrap(); let small_str = "test";
let large_str = "a".repeat(2000);
assert!(serializer.check_size_limit(small_str));
assert!(!serializer.check_size_limit(&large_str));
}
#[test]
fn test_json_serializable_check() {
let serializer = CoreSerializer::new(10.0).unwrap();
let simple_obj = serde_json::json!({"key": "value"});
assert!(serializer.is_json_serializable(&simple_obj));
let null_obj = Value::Null;
assert!(serializer.is_json_serializable(&null_obj));
let array_obj = serde_json::json!([1, 2, 3]);
assert!(serializer.is_json_serializable(&array_obj));
}
#[test]
fn test_nested_reconstruction() {
let serializer = CoreSerializer::new(10.0).unwrap();
let nested_data = serde_json::json!({
"level1": {
"level2": {
"value": "test"
}
}
});
let result = serializer.reconstruct_nested_json(nested_data.clone());
assert!(result.is_ok());
let reconstructed = result.unwrap();
assert_eq!(reconstructed, nested_data);
}
}