mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-03-20 09:01:05 +08:00
# Description of Changes This adds C++ server bindings (/crate/bindings-cpp) to allow writing C++ 20 modules. - Emscripten WASM build system integration with CMake - Macro-based code generation (SPACETIMEDB_TABLE, SPACETIMEDB_REDUCER, etc) - All SpacetimeDB types supported (primitives, Timestamp, Identity, Uuid, etc) - Product types via SPACETIMEDB_STRUCT - Sum types via SPACETIMEDB_ENUM - Constraints marked with FIELD* macros # API and ABI breaking changes None # Expected complexity level and risk 2 - Doesn't heavily impact any other areas but is complex macro C++ structure to support a similar developer experience, did have a small impact on init command # Testing - [x] modules/module-test-cpp - heavily tested every reducer - [x] modules/benchmarks-cpp - tested through the standalone (~6x faster than C#, ~6x slower than Rust) - [x] modules/sdk-test-cpp - [x] modules/sdk-test-procedure-cpp - [x] modules/sdk-test-view-cpp - [x] Wrote several test modules myself - [x] Quickstart smoketest [Currently in progress] - [ ] Write Blackholio C++ server module --------- Signed-off-by: Jason Larabie <jason@clockworklabs.io> Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com> Co-authored-by: Ryan <r.ekhoff@clockworklabs.io> Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
208 lines
7.8 KiB
Python
208 lines
7.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Compare SpacetimeDB module schemas between Rust and C++ implementations.
|
|
|
|
This script compares the JSON schema outputs from:
|
|
- Rust module: rust-module-schema.json
|
|
- C++ module: cpp-module-schema.json or cpp-module-schema-latest.json
|
|
|
|
Usage:
|
|
python3 compare_module_schemas.py
|
|
|
|
To regenerate schema files:
|
|
spacetime describe module-test --json > rust-module-schema.json
|
|
spacetime describe module-test-cpp --json > cpp-module-schema.json
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import os
|
|
|
|
def load_schemas():
|
|
"""Load the schema files, trying both cpp-module-schema.json and cpp-module-schema-latest.json"""
|
|
try:
|
|
with open('rust-module-schema.json', 'r') as f:
|
|
rust = json.load(f)
|
|
except FileNotFoundError:
|
|
print("Error: rust-module-schema.json not found")
|
|
print("Generate it with: spacetime describe module-test --json > rust-module-schema.json")
|
|
sys.exit(1)
|
|
except json.JSONDecodeError as e:
|
|
print(f"Error parsing rust-module-schema.json: {e}")
|
|
print("The file may be corrupted or incomplete. Try regenerating it.")
|
|
sys.exit(1)
|
|
|
|
# Try to load C++ schema, checking both possible filenames
|
|
cpp = None
|
|
for filename in ['cpp-module-schema-latest.json', 'cpp-module-schema.json']:
|
|
if os.path.exists(filename):
|
|
with open(filename, 'r') as f:
|
|
cpp = json.load(f)
|
|
print(f"Loaded C++ schema from {filename}")
|
|
break
|
|
|
|
if cpp is None:
|
|
print("Error: No C++ schema file found")
|
|
print("Generate it with: spacetime describe module-test-cpp --json > cpp-module-schema.json")
|
|
sys.exit(1)
|
|
|
|
return rust, cpp
|
|
|
|
def normalize_index(idx):
|
|
"""Normalize index representation for comparison"""
|
|
name = idx.get('name', {})
|
|
if isinstance(name, dict):
|
|
name = name.get('some', None)
|
|
|
|
accessor = idx.get('accessor_name', {})
|
|
if isinstance(accessor, dict):
|
|
accessor = accessor.get('some', None)
|
|
|
|
algo = idx.get('algorithm', {})
|
|
cols = []
|
|
if isinstance(algo, dict) and 'BTree' in algo:
|
|
cols = algo['BTree']
|
|
|
|
return {'name': name, 'accessor': accessor, 'columns': cols}
|
|
|
|
def compare_tables(rust_tables, cpp_tables):
|
|
"""Compare table definitions between Rust and C++"""
|
|
print("\n=== TABLE COMPARISON ===")
|
|
print(f"Rust tables: {len(rust_tables)}")
|
|
print(f"C++ tables: {len(cpp_tables)}")
|
|
|
|
rust_names = set(rust_tables.keys())
|
|
cpp_names = set(cpp_tables.keys())
|
|
|
|
if rust_names != cpp_names:
|
|
missing = rust_names - cpp_names
|
|
extra = cpp_names - rust_names
|
|
if missing:
|
|
print(f"❌ Missing in C++: {missing}")
|
|
if extra:
|
|
print(f"❌ Extra in C++: {extra}")
|
|
else:
|
|
print("✅ All table names match!")
|
|
|
|
return rust_names & cpp_names
|
|
|
|
def compare_indexes(rust_tables, cpp_tables, common_tables):
|
|
"""Compare index definitions for common tables"""
|
|
print("\n=== INDEX COMPARISON ===")
|
|
|
|
differences = []
|
|
for table_name in sorted(common_tables):
|
|
rust_table = rust_tables[table_name]
|
|
cpp_table = cpp_tables[table_name]
|
|
|
|
rust_indexes = [normalize_index(idx) for idx in rust_table.get('indexes', [])]
|
|
cpp_indexes = [normalize_index(idx) for idx in cpp_table.get('indexes', [])]
|
|
|
|
if rust_indexes != cpp_indexes:
|
|
differences.append((table_name, rust_indexes, cpp_indexes))
|
|
|
|
if not differences:
|
|
print("✅ All table indexes match perfectly!")
|
|
else:
|
|
print(f"Found index differences in {len(differences)} table(s):")
|
|
for table_name, rust_idx, cpp_idx in differences:
|
|
print(f"\n{table_name}:")
|
|
for r in rust_idx:
|
|
name_str = str(r['name']) if r['name'] is not None else "None"
|
|
accessor_str = str(r['accessor']) if r['accessor'] is not None else "None"
|
|
print(f" Rust: name={name_str:<30} accessor={accessor_str:<20} cols={r['columns']}")
|
|
for c in cpp_idx:
|
|
name_str = str(c['name']) if c['name'] is not None else "None"
|
|
accessor_str = str(c['accessor']) if c['accessor'] is not None else "None"
|
|
print(f" C++: name={name_str:<30} accessor={accessor_str:<20} cols={c['columns']}")
|
|
|
|
def compare_reducers(rust_reducers, cpp_reducers):
|
|
"""Compare reducer definitions between Rust and C++"""
|
|
print("\n=== REDUCER COMPARISON ===")
|
|
|
|
rust_names = {r['name'] for r in rust_reducers}
|
|
cpp_names = {r['name'] for r in cpp_reducers}
|
|
|
|
print(f"Rust reducers: {len(rust_names)}")
|
|
print(f"C++ reducers: {len(cpp_names)}")
|
|
|
|
if rust_names != cpp_names:
|
|
missing = rust_names - cpp_names
|
|
extra = cpp_names - rust_names
|
|
if missing:
|
|
print(f"❌ Missing in C++: {missing}")
|
|
if extra:
|
|
print(f"❌ Extra in C++: {extra}")
|
|
else:
|
|
print("✅ All reducer names match!")
|
|
|
|
def check_testf_enum(rust, cpp):
|
|
"""Check the TestF enum specifically since it's known to have issues"""
|
|
print("\n=== TESTF ENUM COMPARISON ===")
|
|
|
|
for schema, lang in [(rust, 'Rust'), (cpp, 'C++')]:
|
|
for reducer in schema['reducers']:
|
|
if reducer['name'] == 'test':
|
|
params = reducer.get('params', [])
|
|
if len(params) >= 4:
|
|
param_type = params[3].get('algebraic_type', {})
|
|
if 'Sum' in param_type:
|
|
variants = param_type['Sum'].get('variants', [])
|
|
print(f"\n{lang} TestF enum:")
|
|
for v in variants:
|
|
name = v.get('name', 'unnamed')
|
|
alg_type = v.get('algebraic_type', {})
|
|
if 'Product' in alg_type and alg_type['Product'].get('fields'):
|
|
fields = alg_type['Product']['fields']
|
|
field_info = []
|
|
for f in fields:
|
|
field_name = f.get('name', 'unnamed')
|
|
field_info.append(field_name)
|
|
print(f" - {name}: has payload with fields {field_info}")
|
|
else:
|
|
print(f" - {name}: unit variant (no payload)")
|
|
break
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("SpacetimeDB Module Schema Comparison")
|
|
print("Comparing Rust vs C++ module implementations")
|
|
print("=" * 60)
|
|
|
|
# Load schemas
|
|
rust, cpp = load_schemas()
|
|
|
|
# Extract tables and reducers
|
|
rust_tables = {t['name']: t for t in rust['tables']}
|
|
cpp_tables = {t['name']: t for t in cpp['tables']}
|
|
rust_reducers = rust['reducers']
|
|
cpp_reducers = cpp['reducers']
|
|
|
|
# Run comparisons
|
|
common_tables = compare_tables(rust_tables, cpp_tables)
|
|
compare_indexes(rust_tables, cpp_tables, common_tables)
|
|
compare_reducers(rust_reducers, cpp_reducers)
|
|
check_testf_enum(rust, cpp)
|
|
|
|
# Summary
|
|
print("\n" + "=" * 60)
|
|
print("SUMMARY")
|
|
print("=" * 60)
|
|
|
|
perfect_match = (
|
|
len(rust_tables) == len(cpp_tables) and
|
|
len(rust_reducers) == len(cpp_reducers) and
|
|
set(rust_tables.keys()) == set(cpp_tables.keys()) and
|
|
{r['name'] for r in rust_reducers} == {r['name'] for r in cpp_reducers}
|
|
)
|
|
|
|
if perfect_match:
|
|
print("✅ Schema structure matches (tables and reducers)")
|
|
print("⚠️ Note: There may be minor differences in:")
|
|
print(" - Index naming conventions (especially multi-column indexes)")
|
|
print(" - TestF enum payload support (C++ currently simplified)")
|
|
else:
|
|
print("❌ Schema structure has differences - review details above")
|
|
|
|
if __name__ == "__main__":
|
|
main() |