Skip to content

Binding architecture ​

xtalforge has one Rust engine and four surfaces. Data crosses a boundary in one of two shapes:

  • Plain data for small values: cell parameters, options, results and errors. These are serde-serializable structs or primitive arrays.
  • Opaque handles for large objects: Structure, Document, trajectories and grids are created, queried and dropped by handle; the data is not copied on every call.

The Rust API is the source of truth. The CLI, Python and WASM bindings call the same functions; no algorithm is reimplemented in a binding.

Per-surface mapping ​

ConcernRustCLIPythonWASM
Large objectstructpath or stdinopaque pyclassopaque class
Coordinates[f64; 3], Vec<[f64; 3]>JSON / textNumPy arrayFloat64Array
Optionstyped structflagskeyword argumentsobject
SuccessResultstdout / JSONreturn valuereturn value
Input errorError::InvalidInputexit 3InputErrorError with code
Usage errorn/aexit 2 (clap)TypeErrorTypeError
Any other error (computation, limit, engine, data)typed variantexit 3XtalforgeErrorError with code

The CLI uses exit 3 for every library error and exit 2 for a clap usage error; the specific cause is the stable code string in the message or in the --json object. Python raises InputError for InvalidInput and the base XtalforgeError for every other variant, with (code, message) as arguments, so callers branch on exc.args[0]. Dedicated limit and engine exception classes and distinct exit codes are not part of the 1.0 surface.

Error contract ​

Every crate has one Error enum with a stable code() string and a message() string. Codes are snake_case and part of the public API. The initial core codes are:

CodeMeaning
invalid_inputa value or geometry violates the preconditions
data_compressiona gzip bundle is corrupt
data_checksuma bundle failed its SHA-256 check
data_digesta bundle manifest digest is malformed
data_jsona bundle payload is not valid JSON for its type
limit_exceededa size check refused the operation
symmetrythe symmetry backend failed

The Python module exposes XtalforgeError as the base class and InputError as the first domain subclass. The exception arguments are (code, message). The WASM module raises a JavaScript Error whose code property is the same string. The CLI prints { "error": { "code", "message" } } with --json and exits with the code in the table.

Handles and lifetimes ​

  • Python: pyo3 classes own the Rust value; methods borrow it for the duration of the call. No Rust reference escapes to Python.
  • WASM: wasm-bindgen classes own the value; the JavaScript object must be dropped explicitly or by scope. No pointer is exposed.
  • CLI: one process, one document; the file is read at the start and written at the end, with a temporary file and an atomic rename.

WASM builds ​

wasm-pack builds the package for web, nodejs and bundler. The default package has no threads. A threaded build is reserved for a later task and will use wasm-bindgen-rayon; it requires cross-origin isolation and must stay opt-in. console_error_panic_hook is installed only in debug builds, so the release .wasm does not carry the hook.

Versioning ​

All four surfaces share the workspace version. scripts/sync-version.sh --check fails on drift. The Python package uses a dynamic version from Cargo; the WASM package.json is generated by wasm-pack and checked by the same script.

Testing ​

tests/vectors/ contains shared JSON vectors. A capability is not at parity until the Rust tests, the CLI runner, pytest and the WASM node tests execute the same vectors. scripts/check-parity.sh compares docs/api/parity.md with the symbols each surface exports and with the API pages.