jsoncompat 0.4.2

JSON Schema and OpenAPI Compatibility Checker
Documentation
help:
  @just --list

check:
  @echo "[just] checking Rust code …"
  cargo fmt --all
  @echo "[just] building Python extension …"
  if [ "$(uname)" = "Darwin" ]; then \
    cargo rustc -p jsoncompat_py --lib -- -C link-arg=-undefined -C link-arg=dynamic_lookup; \
  else \
    cargo build -p jsoncompat_py; \
  fi
  cargo clippy --workspace --all-features --all-targets --locked -- -D warnings
  cargo test --workspace --exclude jsoncompat_py --all-features --locked
  @echo "[just] checking Python code …"
  env -u VIRTUAL_ENV -u UV_DEFAULT_INDEX -u UV_INDEX -u UV_INDEX_URL -u UV_EXTRA_INDEX_URL uv run --no-config --project pybindings --all-extras --group benchmark --locked --with pyright==1.1.408 pyright
  @echo "[just] checking TypeScript code …"
  pnpm --prefix web/jsoncompatdotcom run ci
  pnpm --prefix web/jsoncompatdotcom run build

regen-dataclasses-fixtures:
  JSONCOMPAT_UPDATE_DATACLASSES_FIXTURES=1 cargo test --test dataclasses_fixtures -- --exact dataclass_snapshots_are_up_to_date_for_all_sample_schemas

bench:
  @echo "[just] running Rust benchmarks …"
  cargo bench --workspace --exclude jsoncompat_py --all-features --bench '*' --locked

bench-check:
  @echo "[just] smoke-checking Rust benchmarks …"
  cargo bench --workspace --exclude jsoncompat_py --all-features --bench '*' --locked -- --test

python_bench_command := "env -u VIRTUAL_ENV -u UV_DEFAULT_INDEX -u UV_INDEX -u UV_INDEX_URL -u UV_EXTRA_INDEX_URL PYTHONHASHSEED=0 JSONCOMPAT_NATIVE_PROFILE=release uv run --no-config --project pybindings --all-extras --group benchmark --locked python"

_build-python-release:
  @echo "[just] building release Python extension for representative timings …"
  if [ "$(uname)" = "Darwin" ]; then \
    cargo rustc --release -p jsoncompat_py --lib -- -C link-arg=-undefined -C link-arg=dynamic_lookup; \
  else \
    cargo build --release -p jsoncompat_py; \
  fi

[private]
_verify-dataclass-fixtures:
  @echo "[just] verifying checked-in generated dataclass fixtures are current …"
  cargo test --test dataclasses_fixtures -- --exact dataclass_snapshots_are_up_to_date_for_all_sample_schemas

[private]
_python-bench-provenance profile:
  @mkdir -p target/python-benchmark
  @git status -sb > "target/python-benchmark/provenance-{{profile}}.txt"
  @git rev-parse HEAD >> "target/python-benchmark/provenance-{{profile}}.txt"
  @cat "target/python-benchmark/provenance-{{profile}}.txt"

[private]
_python-bench-runtime iterations repeats profile: _verify-dataclass-fixtures
  @echo "[just] benchmarking generated Python dataclasses against Pydantic v2 …"
  @mkdir -p target/python-benchmark
  {{python_bench_command}} pybindings/bench_dataclasses_runtime.py --iterations {{iterations}} --repeats {{repeats}} > "target/python-benchmark/runtime-{{profile}}.txt"
  @cat "target/python-benchmark/runtime-{{profile}}.txt"

[private]
_python-bench-startup repeats profile: _verify-dataclass-fixtures
  @echo "[just] benchmarking cold generated-model startup against Pydantic v2 …"
  @mkdir -p target/python-benchmark
  {{python_bench_command}} pybindings/bench_dataclasses_startup.py --repeats {{repeats}} > "target/python-benchmark/startup-{{profile}}.txt"
  @cat "target/python-benchmark/startup-{{profile}}.txt"

[private]
_python-bench-scale depth fanout iterations repeats profile: _verify-dataclass-fixtures
  @echo "[just] benchmarking a large recursive dataclass graph against Pydantic v2 …"
  @mkdir -p target/python-benchmark
  {{python_bench_command}} pybindings/bench_dataclasses_scaling.py --depth {{depth}} --fanout {{fanout}} --iterations {{iterations}} --repeats {{repeats}} > "target/python-benchmark/scaling-{{profile}}.txt"
  @cat "target/python-benchmark/scaling-{{profile}}.txt"

[private]
_python-bench-scale-profile depth fanout iterations repeats profile: _verify-dataclass-fixtures
  @echo "[just] profiling checked and trusted recursive JSON deserialization …"
  @mkdir -p target/python-benchmark
  {{python_bench_command}} pybindings/bench_dataclasses_scaling.py --depth {{depth}} --fanout {{fanout}} --iterations {{iterations}} --repeats {{repeats}} --profile > "target/python-benchmark/profile-{{profile}}.txt"
  @cat "target/python-benchmark/profile-{{profile}}.txt"

[private]
_python-bench-fixtures iterations repeats profile: _verify-dataclass-fixtures
  @echo "[just] benchmarking fixture JSON -> generated dataclass -> JSON; comparing semantically equivalent Pydantic v2 models …"
  {{python_bench_command}} pybindings/bench_fixture_models.py --iterations {{iterations}} --repeats {{repeats}} --profile "{{profile}}" --reuse-models --results "target/python-fixture-benchmark/results-{{profile}}.json"

[private]
_python-bench-fixtures-limited iterations repeats limit profile: _verify-dataclass-fixtures
  @echo "[just] smoke-benchmarking fixture JSON -> generated dataclass -> JSON for the first {{limit}} schemas …"
  {{python_bench_command}} pybindings/bench_fixture_models.py --iterations {{iterations}} --repeats {{repeats}} --limit {{limit}} --profile "{{profile}}" --reuse-models --results "target/python-fixture-benchmark/results-{{profile}}.json"

# Benchmark the representative small generated-model graph.
python-bench iterations="10000" repeats="5": _build-python-release (_python-bench-provenance "manual") (_python-bench-runtime iterations repeats "manual")

# Benchmark fresh-interpreter import and first-use costs.
python-bench-startup repeats="25": _build-python-release (_python-bench-provenance "startup") (_python-bench-startup repeats "startup")

# Benchmark the recursive graph at a caller-selected size.
python-bench-scale depth="5" fanout="4" iterations="100" repeats="5": _build-python-release (_python-bench-provenance "manual") (_python-bench-scale depth fanout iterations repeats "manual")

# Benchmark JSON -> generated dataclass -> JSON for every supported fixture and compare semantically equivalent Pydantic peers.
python-bench-fixtures iterations="200" repeats="5": _build-python-release (_python-bench-provenance "manual") (_python-bench-fixtures iterations repeats "manual")

# Quickly smoke-test all four Python benchmark surfaces; fixture E2E timings and ratios cover the first 50 schemas.
python-bench-quick: _build-python-release (_python-bench-provenance "quick") (_python-bench-startup "10" "quick") (_python-bench-runtime "2000" "3" "quick") (_python-bench-scale "4" "3" "25" "3" "quick") (_python-bench-fixtures-limited "25" "3" "50" "quick")

# Run the standard repeatable suite, including every fixture E2E round trip and semantically equivalent Pydantic comparison.
python-bench-standard: _build-python-release (_python-bench-provenance "standard") (_python-bench-startup "25" "standard") (_python-bench-runtime "10000" "5" "standard") (_python-bench-scale "5" "4" "100" "5" "standard") (_python-bench-fixtures "200" "5" "standard")

# Run a high-sample suite over every fixture E2E round trip, with Pydantic comparisons, for release performance reports.
python-bench-full: _build-python-release (_python-bench-provenance "full") (_python-bench-startup "100" "full") (_python-bench-runtime "50000" "10" "full") (_python-bench-scale "6" "4" "100" "10" "full") (_python-bench-fixtures "1000" "10" "full")

# Profile checked and trusted deserialization on the standard recursive graph.
python-bench-profile depth="5" fanout="4" iterations="100" repeats="5": _build-python-release (_python-bench-provenance "profile") (_python-bench-scale-profile depth fanout iterations repeats "standard")

# ---- Basic python smoke test ----

python-demo:
  env -u VIRTUAL_ENV uv run --reinstall-package jsoncompat examples/python/basic/demo.py

# ---- Basic javascript smoke test ----

wasm-demo:
  @command -v wasm-pack >/dev/null 2>&1 || (echo "error: wasm-pack not found (install with 'cargo install wasm-pack --locked')" >&2 && exit 1)
  @command -v uv >/dev/null 2>&1 || (echo "error: uv not found" >&2 && exit 1)
  @echo "[just] building wasm package for the Web target …"
  @wasm-pack build wasm --target web --release
  @echo "[just] serving example at http://localhost:8000/examples/wasm/demo.html …"
  @echo "Press Ctrl+C to stop."
  @env -u VIRTUAL_ENV uv run python -m http.server 8000

release version="patch":
  @echo "[just] releasing {{version}} (dry run)"
  cargo release {{version}} --workspace