import json
import subprocess
from pathlib import Path
from typing import Any
WASM_PATH = Path(__file__).parent.parent.parent.parent / "target/wasm32-wasip1/release/rustledger-wasi.wasm"
class RustledgerWasi:
def __init__(self, wasm_path: Path = WASM_PATH):
self.wasm_path = wasm_path
if not self.wasm_path.exists():
raise FileNotFoundError(f"WASM module not found: {wasm_path}")
def _run(self, command: str, source: str, *args: str) -> dict[str, Any]:
cmd = ["wasmtime", str(self.wasm_path), command, *args]
result = subprocess.run(
cmd,
input=source,
capture_output=True,
text=True,
)
if result.returncode != 0:
raise RuntimeError(f"wasmtime error: {result.stderr}")
return json.loads(result.stdout)
def parse(self, source: str) -> dict[str, Any]:
return self._run("parse", source)
def validate(self, source: str) -> dict[str, Any]:
return self._run("validate", source)
def query(self, source: str, bql: str) -> dict[str, Any]:
return self._run("query", source, bql)
def balances(self, source: str) -> dict[str, Any]:
return self._run("balances", source)
def version(self) -> str:
result = self._run("version", "")
return result["version"]
def main():
ledger = RustledgerWasi()
print(f"rustledger-wasi version: {ledger.version()}")
print()
source = """
2024-01-01 open Assets:Bank USD
2024-01-01 open Expenses:Food USD
2024-01-01 open Expenses:Coffee USD
2024-01-15 * "Coffee Shop" "Morning coffee"
Expenses:Coffee 5.00 USD
Assets:Bank -5.00 USD
2024-01-20 * "Grocery Store" "Weekly groceries"
Expenses:Food 50.00 USD
Assets:Bank -50.00 USD
"""
print("=== Parse ===")
result = ledger.parse(source)
print(f"Directives: {result['directive_count']}")
print(f"Errors: {len(result['errors'])}")
print()
print("=== Validate ===")
result = ledger.validate(source)
print(f"Valid: {result['valid']}")
if result['errors']:
for err in result['errors']:
print(f" Error: {err['message']}")
print()
print("=== Query: Account Balances ===")
result = ledger.query(source, "SELECT account, sum(position) WHERE account ~ 'Expenses' GROUP BY 1")
print(f"Columns: {result['columns']}")
for row in result['rows']:
account = row[0]
balance = row[1]
if 'positions' in balance:
for pos in balance['positions']:
print(f" {account}: {pos['units']['number']} {pos['units']['currency']}")
print()
print("=== Balances ===")
result = ledger.balances(source)
for row in result['rows']:
account = row[0]
balance = row[1]
if 'positions' in balance:
for pos in balance['positions']:
amt = f"{pos['units']['number']} {pos['units']['currency']}"
print(f" {account}: {amt}")
if __name__ == "__main__":
main()