use serde::Serialize;
use serde::de::DeserializeOwned;
pub trait SerializePipeline: Serialize + DeserializeOwned {
fn to_json_value(&self) -> Result<serde_json::Value, serde_json::Error> {
serde_json::to_value(self)
}
fn from_json_value(value: serde_json::Value) -> Result<Self, serde_json::Error> {
serde_json::from_value(value)
}
fn to_json_string(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
}
fn from_json_string(s: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(s)
}
fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(self)
}
}
impl<T> SerializePipeline for T where T: Serialize + DeserializeOwned {}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TestMessage {
content: String,
count: u32,
nested: Option<TestNested>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct TestNested {
value: i32,
tags: Vec<String>,
}
#[test]
fn test_serialize_pipeline_value_roundtrip() {
let msg = TestMessage {
content: "hello world".to_string(),
count: 42,
nested: Some(TestNested {
value: 99,
tags: vec!["a".to_string(), "b".to_string()],
}),
};
let json_value = msg.to_json_value().unwrap();
assert!(json_value.is_object());
assert_eq!(json_value["content"], "hello world");
assert_eq!(json_value["count"], 42);
assert_eq!(json_value["nested"]["value"], 99);
let roundtrip = TestMessage::from_json_value(json_value).unwrap();
assert_eq!(msg, roundtrip);
}
#[test]
fn test_serialize_pipeline_string_roundtrip() {
let msg = TestMessage {
content: "test message".to_string(),
count: 123,
nested: None,
};
let json_str = msg.to_json_string().unwrap();
assert!(!json_str.contains('\n')); assert!(json_str.contains("test message"));
let roundtrip = TestMessage::from_json_string(&json_str).unwrap();
assert_eq!(msg, roundtrip);
}
#[test]
fn test_pretty_print() {
let msg = TestMessage {
content: "pretty test".to_string(),
count: 1,
nested: Some(TestNested {
value: 42,
tags: vec!["tag1".to_string(), "tag2".to_string()],
}),
};
let pretty = msg.to_json_string_pretty().unwrap();
assert!(pretty.contains('\n'), "Should have newlines");
assert!(pretty.contains(" "), "Should have indentation");
assert!(pretty.contains("\"content\": \"pretty test\""));
assert!(pretty.contains("\"count\": 1"));
let roundtrip = TestMessage::from_json_string(&pretty).unwrap();
assert_eq!(msg, roundtrip);
}
#[test]
fn test_empty_optional_fields() {
let msg = TestMessage {
content: "minimal".to_string(),
count: 0,
nested: None,
};
let json_value = msg.to_json_value().unwrap();
assert_eq!(json_value["nested"], serde_json::Value::Null);
let roundtrip = TestMessage::from_json_value(json_value).unwrap();
assert_eq!(msg, roundtrip);
assert!(roundtrip.nested.is_none());
}
#[test]
fn test_nested_structures() {
let msg = TestMessage {
content: "nested test".to_string(),
count: 999,
nested: Some(TestNested {
value: -42,
tags: vec!["tag1".to_string(), "tag2".to_string(), "tag3".to_string()],
}),
};
let json_str = msg.to_json_string().unwrap();
let roundtrip = TestMessage::from_json_string(&json_str).unwrap();
assert_eq!(msg, roundtrip);
let nested = roundtrip.nested.unwrap();
assert_eq!(nested.value, -42);
assert_eq!(nested.tags.len(), 3);
assert_eq!(nested.tags[0], "tag1");
}
#[test]
fn test_invalid_json_string() {
let invalid_json = r#"{"content": "test", invalid}"#;
let result = TestMessage::from_json_string(invalid_json);
assert!(result.is_err());
let err = result.unwrap_err();
let msg = err.to_string();
assert!(!msg.is_empty(), "Error message should not be empty");
}
#[test]
fn test_type_mismatch() {
let json = r#"{"content": "test", "count": "not_a_number", "nested": null}"#;
let result = TestMessage::from_json_string(json);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("invalid type") || err.to_string().contains("expected"));
}
#[test]
fn test_missing_required_field() {
let json = r#"{"content": "test"}"#; let result = TestMessage::from_json_string(json);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("missing field") || err.to_string().contains("count"));
}
#[test]
fn test_extra_fields_ignored() {
let json = r#"{
"content": "test",
"count": 42,
"nested": null,
"extra_field": "ignored",
"another_extra": 123
}"#;
let msg = TestMessage::from_json_string(json).unwrap();
assert_eq!(msg.content, "test");
assert_eq!(msg.count, 42);
assert!(msg.nested.is_none());
}
#[test]
fn test_unicode_handling() {
let msg = TestMessage {
content: "Hello δΈη π".to_string(),
count: 42,
nested: Some(TestNested {
value: 0,
tags: vec!["tag1".to_string(), "ΡΡΠ³2".to_string(), "γΏγ°3".to_string()],
}),
};
let json_str = msg.to_json_string().unwrap();
let roundtrip = TestMessage::from_json_string(&json_str).unwrap();
assert_eq!(msg, roundtrip);
assert_eq!(roundtrip.content, "Hello δΈη π");
}
#[test]
fn test_empty_collections() {
let msg = TestMessage {
content: "empty".to_string(),
count: 0,
nested: Some(TestNested {
value: 1,
tags: vec![], }),
};
let json_value = msg.to_json_value().unwrap();
assert!(json_value["nested"]["tags"].is_array());
assert_eq!(json_value["nested"]["tags"].as_array().unwrap().len(), 0);
let roundtrip = TestMessage::from_json_value(json_value).unwrap();
assert_eq!(msg, roundtrip);
}
#[test]
fn test_large_numbers() {
let msg = TestMessage {
content: "numbers".to_string(),
count: u32::MAX,
nested: Some(TestNested {
value: i32::MIN,
tags: vec![],
}),
};
let json_str = msg.to_json_string().unwrap();
let roundtrip = TestMessage::from_json_string(&json_str).unwrap();
assert_eq!(msg, roundtrip);
assert_eq!(roundtrip.count, u32::MAX);
assert_eq!(roundtrip.nested.unwrap().value, i32::MIN);
}
}