use std::fmt;
use qubit_json::encode::JsonEncoder;
use qubit_json::encode::JsonIntegerSignedness;
use qubit_json::encode::JsonMapKeyKind;
use qubit_json::encode::JsonSerializationErrorCategory;
use qubit_json::encode::JsonSerializationErrorKind;
use qubit_json::value::JsonValueEncoder;
use serde::Serialize;
use serde::Serializer;
use serde::ser::Error as SerializeError;
use serde::ser::SerializeMap;
use serde::ser::SerializeSeq;
use serde::ser::SerializeStruct;
use serde::ser::SerializeStructVariant;
use serde::ser::SerializeTuple;
use serde::ser::SerializeTupleStruct;
use serde::ser::SerializeTupleVariant;
use serde_json::Number;
use serde_json::Value;
use serde_json::from_value;
use serde_json::json;
use serde_json::to_string;
use serde_json::to_vec;
use serde_json::value::RawValue;
struct DuplicateKeyProbe;
impl Serialize for DuplicateKeyProbe {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("same", &1_u8)?;
map.serialize_entry("same", &2_u8)?;
map.end()
}
}
struct FloatKeyProbe(f64);
impl Serialize for FloatKeyProbe {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry(&self.0, &true)?;
map.end()
}
}
struct MapKeyProbe(u8);
impl Serialize for MapKeyProbe {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self.0 {
0 => serializer.serialize_bool(true),
1 => serializer.serialize_i8(-8),
2 => serializer.serialize_i16(-16),
3 => serializer.serialize_i32(-32),
4 => serializer.serialize_i64(-64),
5 => serializer.serialize_i128(-128),
6 => serializer.serialize_u8(8),
7 => serializer.serialize_u16(16),
8 => serializer.serialize_u32(32),
9 => serializer.serialize_u64(64),
10 => serializer.serialize_u128(128),
11 => serializer.serialize_f32(1.5),
12 => serializer.serialize_f64(2.5),
13 => serializer.serialize_char('x'),
14 => serializer.serialize_str("text"),
15 => serializer.serialize_bytes(&[1, 2]),
16 => serializer.serialize_none(),
17 => serializer.serialize_some(&1_i32),
18 => serializer.serialize_unit(),
19 => serializer.serialize_unit_struct("Unit"),
20 => serializer.serialize_unit_variant("Enum", 0, "Unit"),
21 => serializer.serialize_newtype_struct("New", &-1_i32),
22 => serializer.serialize_newtype_variant("Enum", 0, "New", &1_i32),
23 => {
let _ = serializer.serialize_seq(Some(0))?;
unreachable!("map-key serializer must reject sequences")
}
24 => {
let _ = serializer.serialize_tuple(0)?;
unreachable!("map-key serializer must reject tuples")
}
25 => {
let _ = serializer.serialize_tuple_struct("Tuple", 0)?;
unreachable!("map-key serializer must reject tuple structs")
}
26 => {
let _ = serializer.serialize_tuple_variant("Enum", 0, "Tuple", 0)?;
unreachable!("map-key serializer must reject tuple variants")
}
27 => {
let _ = serializer.serialize_map(Some(0))?;
unreachable!("map-key serializer must reject maps")
}
28 => {
let _ = serializer.serialize_struct("Object", 0)?;
unreachable!("map-key serializer must reject structs")
}
29 => {
let _ = serializer.serialize_struct_variant("Enum", 0, "Object", 0)?;
unreachable!("map-key serializer must reject struct variants")
}
30 => serializer.collect_str(&DisplayProbe),
31 => serializer.serialize_f32(f32::NAN),
_ => serializer.serialize_f64(f64::NAN),
}
}
}
struct MapKeyContainer(u8);
impl Serialize for MapKeyContainer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry(&MapKeyProbe(self.0), &true)?;
map.end()
}
}
enum ContractMapKey {
Signed(i128),
Unsigned(u128),
Present(i32),
Newtype(i32),
}
impl Serialize for ContractMapKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Signed(value) => serializer.serialize_i128(*value),
Self::Unsigned(value) => serializer.serialize_u128(*value),
Self::Present(value) => serializer.serialize_some(value),
Self::Newtype(value) => serializer.serialize_newtype_struct("Key", value),
}
}
}
struct ContractMapKeyContainer(ContractMapKey);
impl Serialize for ContractMapKeyContainer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry(&self.0, &true)?;
map.end()
}
}
struct NestedNonFiniteProbe;
impl Serialize for NestedNonFiniteProbe {
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Err(SerializeError::custom("non-finite floating-point value"))
}
}
struct FailingDisplayProbe;
impl fmt::Display for FailingDisplayProbe {
fn fmt(&self, _formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
Err(fmt::Error)
}
}
impl Serialize for FailingDisplayProbe {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
struct InvalidMapStateProbe(u8);
impl Serialize for InvalidMapStateProbe {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
match self.0 {
0 => map.serialize_value(&true)?,
1 => {
map.serialize_key("first")?;
map.serialize_key("second")?;
}
_ => map.serialize_key("pending")?,
}
map.end()
}
}
struct SerializerProbe(u8);
impl Serialize for SerializerProbe {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self.0 {
0 => serializer.serialize_bool(true),
1 => serializer.serialize_i8(-1),
2 => serializer.serialize_i16(-1),
3 => serializer.serialize_i32(-1),
4 => serializer.serialize_i64(-1),
5 => serializer.serialize_i128(-1),
6 => serializer.serialize_u8(1),
7 => serializer.serialize_u16(1),
8 => serializer.serialize_u32(1),
9 => serializer.serialize_u64(1),
10 => serializer.serialize_u128(1),
11 => serializer.serialize_f32(1.0),
12 => serializer.serialize_f64(1.0),
13 => serializer.serialize_char('x'),
14 => serializer.serialize_str("x"),
15 => serializer.serialize_bytes(&[1, 2]),
16 => serializer.serialize_none(),
17 => serializer.serialize_some(&1_i32),
18 => serializer.serialize_unit(),
19 => serializer.serialize_unit_struct("Unit"),
20 => serializer.serialize_unit_variant("Enum", 0, "Unit"),
21 => serializer.serialize_newtype_struct("New", &1_i32),
22 => serializer.serialize_newtype_variant("Enum", 0, "New", &1_i32),
23 => {
let mut sequence = serializer.serialize_seq(Some(1))?;
sequence.serialize_element(&1_i32)?;
sequence.end()
}
24 => {
let mut tuple = serializer.serialize_tuple(1)?;
tuple.serialize_element(&1_i32)?;
tuple.end()
}
25 => {
let mut tuple = serializer.serialize_tuple_struct("Tuple", 1)?;
tuple.serialize_field(&1_i32)?;
tuple.end()
}
26 => {
let mut tuple = serializer.serialize_tuple_variant("Enum", 0, "Tuple", 1)?;
tuple.serialize_field(&1_i32)?;
tuple.end()
}
27 => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("key", &1_i32)?;
map.end()
}
28 => {
let mut object = serializer.serialize_struct("Object", 1)?;
object.serialize_field("key", &1_i32)?;
object.end()
}
29 => {
let mut object = serializer.serialize_struct_variant("Enum", 0, "Object", 1)?;
object.serialize_field("key", &1_i32)?;
object.end()
}
_ => serializer.collect_str(&DisplayProbe),
}
}
}
struct DisplayProbe;
impl fmt::Display for DisplayProbe {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("display")
}
}
#[test]
fn test_json_value_encoder_encodes_nested_value() {
let encoder = JsonValueEncoder::new();
let actual = encoder
.encode(&json!({"items": [1, true, null], "name": "Ada"}))
.expect("strict JSON-compatible values should encode");
assert_eq!(actual, json!({"items": [1, true, null], "name": "Ada"}));
}
#[test]
fn test_json_value_encoder_preserves_integer_boundaries() {
let encoder = JsonValueEncoder::default();
assert_eq!(encoder.encode(&i64::MIN), Ok(json!(i64::MIN)));
assert_eq!(encoder.encode(&u64::MAX), Ok(json!(u64::MAX)));
assert_eq!(encoder.encode(&(i64::MIN as i128)), Ok(json!(i64::MIN)));
assert_eq!(encoder.encode(&(u64::MAX as u128)), Ok(json!(u64::MAX)));
}
#[test]
fn test_json_value_encoder_rejects_wide_integers() {
let encoder = JsonValueEncoder::new();
let signed = encoder.encode(&i128::MAX).expect_err("wide signed integer must fail");
let unsigned = encoder.encode(&u128::MAX).expect_err("wide unsigned integer must fail");
assert_eq!(
signed.kind(),
JsonSerializationErrorKind::IntegerOutOfRange {
signedness: JsonIntegerSignedness::Signed,
}
);
assert_eq!(signed.category(), JsonSerializationErrorCategory::Number);
assert!(signed.is_number_error());
assert_eq!(signed.integer_signedness(), Some(JsonIntegerSignedness::Signed));
assert_eq!(
unsigned.kind(),
JsonSerializationErrorKind::IntegerOutOfRange {
signedness: JsonIntegerSignedness::Unsigned,
}
);
}
#[test]
fn test_json_value_encoder_rejects_non_finite_floats() {
let encoder = JsonValueEncoder::new();
for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
let error = encoder.encode(&value).expect_err("non-finite float must fail");
assert_eq!(error.kind(), JsonSerializationErrorKind::NonFiniteFloat);
}
let custom = encoder
.encode(&NestedNonFiniteProbe)
.expect_err("custom serializer failure must remain custom");
assert_eq!(custom.kind(), JsonSerializationErrorKind::CustomSerialization);
assert_eq!(custom.category(), JsonSerializationErrorCategory::Custom);
assert!(!custom.to_string().contains("non-finite floating-point value"));
}
#[test]
fn test_json_value_encoder_preserves_float32_semantics() {
let encoder = JsonValueEncoder::new();
let source = f32::from_bits(0xC65B_9806);
let projected = encoder.encode(&source).expect("finite f32 should encode");
let text = to_string(&projected).expect("encoded JSON value should render");
let widened = Value::Number(Number::from_f64(f64::from(source)).expect("source is finite"));
assert_eq!(
from_value::<f32>(projected).expect("projected f32 should decode"),
source
);
assert_ne!(text, to_string(&widened).expect("widened number should render"));
}
#[test]
fn test_json_value_encoder_encodes_finite_float_map_key() {
assert_eq!(
JsonValueEncoder::new().encode(&FloatKeyProbe(1.5)),
Ok(json!({"1.5": true})),
);
}
#[test]
fn test_json_value_encoder_matches_text_encoder_float_map_keys() {
let value_encoder = JsonValueEncoder::new();
let mut text_encoder = JsonEncoder::unlimited();
for key in [-0.0, 1.0, 1.5, f64::MIN_POSITIVE, f64::MAX] {
let probe = FloatKeyProbe(key);
let value = value_encoder.encode(&probe).expect("finite key should encode");
let value_text = to_vec(&value).expect("JSON value should render");
let direct_text = text_encoder.to_vec(&probe).expect("finite key should encode as text");
assert_eq!(value_text, direct_text, "map key differs for {key}");
}
}
#[test]
fn test_json_value_encoder_matches_text_encoder_wide_and_wrapped_map_keys() {
let cases = [
(ContractMapKey::Signed(i128::MIN), i128::MIN.to_string()),
(ContractMapKey::Signed(i128::MAX), i128::MAX.to_string()),
(ContractMapKey::Unsigned(u128::MAX), u128::MAX.to_string()),
(ContractMapKey::Present(-7), String::from("-7")),
(ContractMapKey::Newtype(9), String::from("9")),
];
let value_encoder = JsonValueEncoder::new();
let mut text_encoder = JsonEncoder::unlimited();
for (key, expected_key) in cases {
let probe = ContractMapKeyContainer(key);
let value = value_encoder
.encode(&probe)
.expect("contract map key should encode as a JSON value");
let value_text = to_vec(&value).expect("encoded JSON value should render");
let direct_text = text_encoder
.to_vec(&probe)
.expect("contract map key should encode as strict text");
assert_eq!(value, json!({expected_key: true}));
assert_eq!(value_text, direct_text, "map-key entry points must agree");
}
}
#[test]
fn test_json_value_encoder_rejects_non_finite_float_map_key() {
let error = JsonValueEncoder::new()
.encode(&FloatKeyProbe(f64::NAN))
.expect_err("non-finite map key must fail");
assert_eq!(error.kind(), JsonSerializationErrorKind::NonFiniteFloat);
}
#[test]
fn test_json_value_encoder_supports_scalar_map_key_entry_points() {
const EXPECTED_KEYS: [&str; 19] = [
"true", "-8", "-16", "-32", "-64", "-128", "8", "16", "32", "64", "128", "1.5", "2.5", "x", "text", "1",
"Unit", "-1", "display",
];
let encoder = JsonValueEncoder::new();
let supported_indices = (0_u8..=14).chain([17, 20, 21, 30]);
for (index, expected_key) in supported_indices.zip(EXPECTED_KEYS) {
let encoded = encoder
.encode(&MapKeyContainer(index))
.expect("supported scalar map key should encode");
assert_eq!(
encoded,
json!({expected_key: true}),
"unexpected map key for entry point {index}"
);
}
}
#[test]
fn test_json_value_encoder_rejects_unsupported_map_key_entry_points() {
let encoder = JsonValueEncoder::new();
let unsupported_indices = (15_u8..=16).chain(18..=19).chain(22..=29);
for index in unsupported_indices {
let error = encoder
.encode(&MapKeyContainer(index))
.expect_err("unsupported entry point must not produce a JSON object key");
assert!(matches!(
error.kind(),
JsonSerializationErrorKind::UnsupportedMapKey { .. }
));
assert_eq!(error.category(), JsonSerializationErrorCategory::ObjectKey);
assert!(error.is_map_key_error());
assert!(
error.map_key_kind().is_some(),
"missing map-key kind for entry point {index}"
);
}
assert_eq!(
encoder
.encode(&MapKeyContainer(15))
.expect_err("byte key must fail")
.map_key_kind(),
Some(JsonMapKeyKind::Bytes)
);
assert_eq!(
encoder
.encode(&MapKeyContainer(23))
.expect_err("sequence key must fail")
.map_key_kind(),
Some(JsonMapKeyKind::Sequence)
);
}
#[test]
fn test_json_value_encoder_rejects_non_finite_map_key_entry_points() {
let encoder = JsonValueEncoder::new();
for index in [31, 32] {
let error = encoder
.encode(&MapKeyContainer(index))
.expect_err("non-finite map key must fail");
assert_eq!(
error.kind(),
JsonSerializationErrorKind::NonFiniteFloat,
"entry point {index} must preserve the non-finite classification",
);
}
}
#[test]
fn test_json_value_encoder_rejects_duplicate_object_key() {
let error = JsonValueEncoder::new()
.encode(&DuplicateKeyProbe)
.expect_err("duplicate key must fail");
assert_eq!(error.kind(), JsonSerializationErrorKind::DuplicateObjectKey);
assert_eq!(error.category(), JsonSerializationErrorCategory::ObjectKey);
}
#[test]
fn test_json_value_encoder_materializes_raw_value() {
let raw =
RawValue::from_string(String::from(r#"{"ok":true,"values":[1,2]}"#)).expect("fixture should be valid raw JSON");
let actual = JsonValueEncoder::new()
.encode(&raw)
.expect("strict RawValue should materialize");
assert_eq!(actual, json!({"ok": true, "values": [1, 2]}));
}
#[test]
fn test_json_value_encoder_rejects_wide_raw_value_number() {
let raw = RawValue::from_string(String::from("18446744073709551616"))
.expect("serde_json RawValue accepts syntactically valid wide integers");
let error = JsonValueEncoder::new()
.encode(&raw)
.expect_err("wide RawValue number must fail");
assert_eq!(error.kind(), JsonSerializationErrorKind::InvalidRawValue);
assert_eq!(error.category(), JsonSerializationErrorCategory::RawValue);
assert!(error.is_raw_value_error());
}
#[test]
fn test_json_value_encoder_classifies_display_formatting_failure() {
let error = JsonValueEncoder::new()
.encode(&FailingDisplayProbe)
.expect_err("failing Display must produce an error");
assert_eq!(error.kind(), JsonSerializationErrorKind::DisplayFormattingFailed);
assert_eq!(error.category(), JsonSerializationErrorCategory::SerializerContract);
}
#[test]
fn test_json_value_encoder_classifies_invalid_map_states() {
use qubit_json::encode::JsonSerializerStateError;
let encoder = JsonValueEncoder::new();
let expected = [
JsonSerializerStateError::MapValueWithoutKey,
JsonSerializerStateError::MapKeyAlreadyPending,
JsonSerializerStateError::MapEndedWithPendingKey,
];
for (index, reason) in expected.into_iter().enumerate() {
let error = encoder
.encode(&InvalidMapStateProbe(index as u8))
.expect_err("invalid map state must fail");
assert_eq!(
error.kind(),
JsonSerializationErrorKind::InvalidSerializerState { reason }
);
assert!(error.is_serializer_contract_error());
assert_eq!(error.serializer_state_error(), Some(reason));
}
}
#[test]
fn test_json_value_encoder_preserves_former_number_marker_object() {
const MARKER: &str = concat!("$", "serde_json", "::private::Number");
let source = json!({MARKER: "123"});
assert_eq!(JsonValueEncoder::new().encode(&source), Ok(source));
}
#[test]
fn test_json_value_encoder_supports_all_serde_entry_points() {
let encoder = JsonValueEncoder::new();
for index in 0..=30 {
encoder
.encode(&SerializerProbe(index))
.expect("supported Serde entry point should encode");
}
}