# Storage engine playground helpers. HAS_CARGO := $(wildcard Cargo.toml) # Workspace crates discovered under `crates/*/Cargo.toml`. The directory name is # assumed to match the package name (the convention documented in crates/README.md). CRATE_MANIFESTS := $(wildcard crates/*/Cargo.toml) CRATES := $(patsubst crates/%/Cargo.toml,%,$(CRATE_MANIFESTS)) CRATE_FLAGS := $(addprefix -p ,$(CRATES)) .DEFAULT_GOAL := help .PHONY: help help: ## Show help messages for all available targets @grep -E '^[a-zA-Z_-]+:.*## .*$$' Makefile | \ awk 'BEGIN {FS = ":.*## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' .PHONY: format format: ## Apply code formatting @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Skipping Rust formatting."; \ else \ cargo fmt --all; \ fi .PHONY: format-check format-check: ## Check code formatting without applying changes @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Skipping Rust format check."; \ else \ cargo fmt --all --check; \ fi .PHONY: lint lint: ## Run linters across every crate under crates/ @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Skipping lint."; \ elif [ -z "$(CRATES)" ]; then \ echo "No crates/*/Cargo.toml found. Skipping lint."; \ else \ cargo clippy $(CRATE_FLAGS) --no-deps -- -D warnings -D clippy::unwrap_used -D clippy::expect_used; \ fi .PHONY: test test: ## Run unit, integration, and doc tests across all features (no benches) @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Skipping tests."; \ else \ cargo test --all-features; \ fi .PHONY: bench bench: ## Run benchmarks across all features @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Skipping benchmarks."; \ else \ cargo bench --all-features; \ fi .PHONY: bench-check bench-check: ## Type-check benchmark code without running it @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Skipping bench check."; \ else \ cargo check --benches --all-features --quiet; \ fi .PHONY: check check: format-check lint test bench-check viewer-test ## Run all checks (format-check, lint, test, bench-check, viewer-test) .PHONY: clean clean: ## Remove build output @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Nothing to clean."; \ else \ cargo clean; \ fi EXPORTER_DIR := tools/exporter EXPORTER_FIXTURES := crates/plan-runner/fixtures EXAMPLES_DIR := $(EXPORTER_DIR)/examples .PHONY: export-fixtures export-fixtures: ## Regenerate plan JSON for every tools/exporter/examples/*.scenario.json (needs Cabal and GHC; use `make shell` first). @if ! command -v cabal >/dev/null 2>&1; then \ echo "cabal not found. Enter the dev shell with 'make shell' (or 'nix develop') first."; \ exit 1; \ fi @cd $(EXPORTER_DIR) && cabal build plan-export @mkdir -p $(EXPORTER_FIXTURES) @for sc in $(EXAMPLES_DIR)/*.scenario.json; do \ base=$$(basename $$sc .scenario.json); \ out=$(EXPORTER_FIXTURES)/$$base.json; \ echo "exporting $$sc -> $$out"; \ (cd $(EXPORTER_DIR) && cabal run -v0 plan-export -- examples/$$base.scenario.json) > $$out; \ done .PHONY: examples examples: export-fixtures ## Regenerate fixtures from scenarios and run them through plan-runner against their oracles. @cargo test -p plan-runner --test examples DEMO_SCENARIO ?= two_atom_join .PHONY: demo demo: ## Run the end-to-end demo: query -> geolog plan -> Geomerge storage -> execution -> verified results @if [ -z "$(HAS_CARGO)" ]; then \ echo "No Cargo.toml found. Skipping demo."; \ exit 1; \ fi @echo "=== End-to-end demo: query -> plan -> Geomerge -> results ===" @echo "" @python3 -c 'import json, sys; \ s = json.load(open(sys.argv[1])); \ arity = lambda t: len(s["schema"][t]["columns"]); \ term = lambda a, i: a["values"].get(str(i), {}).get("var", "_"); \ atoms = ", ".join(a["table"] + "(" + ", ".join(term(a, i) for i in range(arity(a["table"]))) + ")" for a in s["atoms"]); \ print("query (" + sys.argv[1] + "):"); \ print(" " + atoms)' tools/exporter/examples/$(DEMO_SCENARIO).scenario.json @echo "" @echo "The committed plan below was produced from this query by the geolog" @echo "planner via tools/exporter (make export-fixtures regenerates it)." @echo "" @result=$$(cargo run -q -p plan-runner -- --trace --backend geomerge crates/plan-runner/fixtures/$(DEMO_SCENARIO).json); \ echo ""; \ echo "result bindings:"; \ echo "$$result" | python3 -m json.tool --indent 2 @echo "" @echo "Inspect the plan visually: run 'make viewer' and open" @echo " http://localhost:$(VIEWER_PORT)/tools/plan-viewer/index.html?fixture=../../crates/plan-runner/fixtures/$(DEMO_SCENARIO).json" .PHONY: viewer-test viewer-test: ## Check the plan viewer's JS engine against every fixture oracle (needs Node) @if ! command -v node >/dev/null 2>&1; then \ echo "node not found. Skipping plan viewer engine check."; \ else \ node tools/plan-viewer/test.js; \ fi VIEWER_PORT ?= 8000 .PHONY: viewer viewer: ## Serve the repository over HTTP for the plan viewer (override the port with VIEWER_PORT=...) @if ! command -v python3 >/dev/null 2>&1; then \ echo "python3 not found. Enter the dev shell with 'make shell' (or 'nix develop') first."; \ exit 1; \ fi @echo "Plan viewer: http://localhost:$(VIEWER_PORT)/tools/plan-viewer/index.html" @echo "Example: http://localhost:$(VIEWER_PORT)/tools/plan-viewer/index.html?fixture=../../crates/plan-runner/fixtures/two_atom_join.json" @python3 -m http.server $(VIEWER_PORT) .PHONY: shell shell: ## Enter the Nix dev shell defined in flake.nix @nix develop .PHONY: setup-hooks setup-hooks: ## Install Git hooks with pre-commit @pre-commit install --hook-type pre-commit @pre-commit install --hook-type pre-push @pre-commit install-hooks .PHONY: test-hooks test-hooks: ## Run pre-commit hooks across all files @pre-commit run --all-files --show-diff-on-failure