qubit-json 0.8.1

Resource-aware infrastructure for lenient and strict JSON processing
Documentation
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Tests compound budget-aware JSON serialization.

use qubit_budget::json::JsonEncodeLimits;
use qubit_budget::json::JsonEncodeSession;
use qubit_budget::json::JsonResource;
use serde::Serialize;
use serde::Serializer;

use crate::encode::json_encode_test_support::encode;

#[derive(Serialize)]
struct Pair {
    left: u8,
    right: u8,
}

#[derive(Serialize)]
struct Triple(u8, u8);

struct Bytes;

impl Serialize for Bytes {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_bytes(&[1, 10, 100])
    }
}

/// Byte values spanning every decimal-width branch used by JSON accounting.
struct BoundaryBytes;

impl Serialize for BoundaryBytes {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_bytes(&[9, 10, 99, 100, 255])
    }
}

/// Verifies compound serialization preserves object members.
#[test]
fn test_json_encode_compound_serializes_struct_members() {
    let mut session = JsonEncodeSession::from_limits(JsonEncodeLimits::<JsonResource, usize>::builder().build());
    let output = encode(&Pair { left: 1, right: 2 }, &mut session).expect("compound JSON should serialize");

    assert_eq!(output, br#"{"left":1,"right":2}"#);
}

/// Verifies tuple-struct compounds complete through the decorated end path.
#[test]
fn test_json_encode_compound_serializes_tuple_struct_fields() {
    let mut session = JsonEncodeSession::from_limits(JsonEncodeLimits::<JsonResource, usize>::builder().build());
    let output = encode(&Triple(3, 4), &mut session).expect("tuple-struct JSON should serialize");

    assert_eq!(output, b"[3,4]");
}

/// Verifies byte-array numbers use their exact decimal widths for both the
/// point number limit and the cumulative value-payload limit.
#[test]
fn test_json_encode_bytes_charge_decimal_width_boundaries() {
    let exact_limits = JsonEncodeLimits::<JsonResource, usize>::builder()
        .max_number_bytes(3)
        .max_payload_bytes(11)
        .build();
    let mut exact = JsonEncodeSession::from_limits(exact_limits);

    assert_eq!(
        encode(&BoundaryBytes, &mut exact).expect("exact byte budgets must fit"),
        b"[9,10,99,100,255]",
    );

    let number_limits = JsonEncodeLimits::<JsonResource, usize>::builder()
        .max_number_bytes(2)
        .build();
    let mut short_number = JsonEncodeSession::from_limits(number_limits);
    assert!(
        encode(&BoundaryBytes, &mut short_number).is_err(),
        "100 must cross the two-to-three-digit number boundary",
    );

    let payload_limits = JsonEncodeLimits::<JsonResource, usize>::builder()
        .max_payload_bytes(10)
        .build();
    let mut short_payload = JsonEncodeSession::from_limits(payload_limits);
    assert!(
        encode(&BoundaryBytes, &mut short_payload).is_err(),
        "decimal byte widths must consume eleven payload bytes",
    );
}

/// Verifies scalar serializer paths instantiate with byte-sized quantities.
#[test]
fn test_json_encode_scalar_paths_support_u8_quantities() {
    macro_rules! assert_scalar {
        ($value:expr) => {{
            let mut session = JsonEncodeSession::<JsonResource, u8>::from_limits(
                JsonEncodeLimits::<JsonResource, u8>::builder().build(),
            );
            encode(&$value, &mut session).expect("scalar should fit an unconfigured budget");
        }};
    }

    assert_scalar!(-1_i8);
    assert_scalar!(-1_i16);
    assert_scalar!(-1_i32);
    assert_scalar!(-1_i64);
    assert_scalar!(-1_i128);
    assert_scalar!(1_u8);
    assert_scalar!(1_u16);
    assert_scalar!(1_u32);
    assert_scalar!(1_u64);
    assert_scalar!(1_u128);
    assert_scalar!(1.5_f32);
    assert_scalar!(2.5_f64);
    assert_scalar!('x');
    assert_scalar!("text");
    assert_scalar!(Some(1_u8));
    assert_scalar!(None::<u8>);
    assert_scalar!(());
    assert_scalar!(vec![1_u8]);
    assert_scalar!(Bytes);
}