use serde::Serialize;
use serde_json::Value;
pub struct RjangoJsonEncoder;
impl RjangoJsonEncoder {
pub fn encode<T: Serialize>(value: &T) -> Result<String, serde_json::Error> {
serde_json::to_string(value)
}
pub fn encode_pretty<T: Serialize>(value: &T) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(value)
}
pub fn to_value<T: Serialize>(value: &T) -> Result<Value, serde_json::Error> {
serde_json::to_value(value)
}
}
#[cfg(test)]
mod tests {
use super::RjangoJsonEncoder;
use chrono::{NaiveDate, TimeZone, Utc};
use serde::Serialize;
use serde_json::json;
use uuid::Uuid;
#[derive(Debug, Serialize)]
struct ExamplePayload {
name: String,
count: usize,
}
#[derive(Debug, Serialize)]
struct TemporalPayload {
date: NaiveDate,
timestamp: chrono::DateTime<Utc>,
id: Uuid,
}
#[test]
fn encode_serializes_plain_structs() {
let encoded = RjangoJsonEncoder::encode(&ExamplePayload {
name: "Rjango".to_string(),
count: 3,
})
.expect("plain structs should serialize");
assert_eq!(encoded, "{\"name\":\"Rjango\",\"count\":3}");
}
#[test]
fn encode_pretty_serializes_with_indentation() {
let encoded = RjangoJsonEncoder::encode_pretty(&ExamplePayload {
name: "Rjango".to_string(),
count: 3,
})
.expect("plain structs should serialize with pretty formatting");
assert!(encoded.contains('\n'));
assert!(encoded.contains(" \"name\": \"Rjango\""));
}
#[test]
fn encode_supports_dates_datetimes_and_uuids() {
let payload = TemporalPayload {
date: NaiveDate::from_ymd_opt(2024, 2, 29).expect("test date should be valid"),
timestamp: Utc.with_ymd_and_hms(2024, 2, 29, 12, 30, 45).unwrap(),
id: Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")
.expect("test UUID should parse"),
};
let encoded =
RjangoJsonEncoder::encode(&payload).expect("temporal payload should serialize");
assert!(encoded.contains("\"date\":\"2024-02-29\""));
assert!(encoded.contains("\"timestamp\":\"2024-02-29T12:30:45Z\""));
assert!(encoded.contains("\"id\":\"550e8400-e29b-41d4-a716-446655440000\""));
}
#[test]
fn to_value_converts_serializable_types_to_json_values() {
let value = RjangoJsonEncoder::to_value(&ExamplePayload {
name: "Rjango".to_string(),
count: 3,
})
.expect("plain structs should convert to JSON values");
assert_eq!(value, json!({"name": "Rjango", "count": 3}));
}
#[test]
fn encode_handles_existing_json_values() {
let encoded = RjangoJsonEncoder::encode(&json!({"ok": true, "items": [1, 2, 3]}))
.expect("serde_json::Value should serialize");
assert_eq!(encoded, "{\"items\":[1,2,3],\"ok\":true}");
}
#[test]
fn encode_reports_serializer_errors_for_non_string_map_keys() {
use std::collections::BTreeMap;
let mut payload = BTreeMap::new();
payload.insert((1, 2), "value");
let error = RjangoJsonEncoder::encode(&payload)
.expect_err("tuple map keys are not valid JSON object keys");
assert!(error.to_string().contains("key must be a string"));
}
}