Skip to content

Contributing


Development workflow

# Build
cargo build --workspace

# Test
cargo test --workspace

# Lint (warnings are errors)
cargo clippy --workspace --all-targets -- -D warnings

# Format
cargo fmt --all

Pre-commit hooks

Pre-commit runs on every commit:

  1. cargo fmt — formatting check
  2. cargo clippy — lint with warnings as errors
  3. cargo test — full test suite

All three must pass before the commit is created.


Testing

Unit tests

Each crate contains unit tests alongside the source code. Run with:

cargo test -p calculon-costing
cargo test -p calculon-tiles

Integration tests

Integration tests use real Monaco graph tiles committed in test_data/monaco_tiles/. These tiles cover ~32 km² and enable deterministic routing tests.

# Route integration tests
cargo test -p calculon-route --test monaco_route

# Matrix integration tests
cargo test -p calculon-matrix --test monaco_matrix

# HTTP integration tests
cargo test -p calculon --test http_integration

Some tests require France tiles (test_data/france_tiles/) which are not committed to the repository. These tests skip automatically when the tiles are not present.


Project structure

calculon/
├── crates/
│   ├── calculon-core/       # GraphId, PointLL, constants
│   ├── calculon-tiles/      # Tile reader, GraphReader, NodeInfo, DirectedEdge
│   ├── calculon-costing/    # DynamicCost, AutoCost, BicycleCost, PedestrianCost
│   ├── calculon-route/      # Bidirectional A*
│   ├── calculon-matrix/     # CostMatrix
│   └── calculon/            # HTTP server, handlers, maneuvers, narrative
├── test_data/
│   └── monaco_tiles/        # Real tiles for integration tests
├── locales/                 # i18n narrative files (36+ languages)
├── tools/
│   └── compare/             # Comparison UI (Lit + MapLibre)
└── docs/                    # This documentation (Zensical)

Code conventions

  • No eprintln! — use the logger (log::debug!, log::trace!, etc.)
  • Graph-walking helpers belong on GraphReader in calculon-tiles, not duplicated across crates
  • unsafe is only used in tile parsing with documented bounds checks
  • Pre-computed lookup tables for O(1) factor lookups — avoid runtime computation in hot paths
  • spawn_blocking for all CPU-bound work in HTTP handlers

Benchmarks

# All benchmarks
cargo bench --workspace

# Specific benchmark
cargo bench -p calculon-route -- route_city
cargo bench -p calculon-costing -- edge_cost

Benchmarks use Criterion and require test_data/france_tiles/ for routing benchmarks.


Coverage

# Generate coverage report (requires cargo-llvm-cov)
make coverage

The report is written to lcov-fixed.info (with relative paths for SonarQube compatibility).


SonarQube

Local SonarQube analysis is available via Docker:

# One-time: create the project
make sonar-create-project

# Run analysis with coverage
make sonar

This runs cargo clippy (JSON output), generates LCOV coverage, and launches the SonarQube scanner. Results are available at http://localhost:9000.

Quality gates target: zero bugs, zero vulnerabilities, zero code smells, coverage >= 80%.


Debugging

Logging

Use RUST_LOG to control log output:

# Route-level debug logging
RUST_LOG=calculon_route=debug calculon --tile-dir ./tiles

# Costing trace logging
RUST_LOG=calculon_costing=trace calculon --tile-dir ./tiles

# Everything
RUST_LOG=debug calculon --tile-dir ./tiles

Always use log::debug!, log::trace!, etc. — never eprintln!.

Debug endpoints

Use the debug endpoints to inspect graph state at specific locations:

# Inspect all edges at a junction
curl -s http://localhost:8002/debug/inspect \
  -d '{"lat": 43.611, "lon": 3.862}' | jq

# Cost breakdown from a specific edge
curl -s http://localhost:8002/debug/cost \
  -d '{"from_edge_id": "2/751073/456"}' | jq

# Verbose route with per-edge costs
curl -s http://localhost:8002/route \
  -d '{"locations": [{"lat":43.62,"lon":3.84},{"lat":43.60,"lon":3.88}], "verbose": true}' \
  | jq '.trip.legs[0].debug_edges'

See API Endpoints for full request/response formats.

Comparison tool

For side-by-side comparison with Valhalla, use the comparison tool:

./tools/compare/run.sh --tiles france

Obtaining tiles

Monaco (test tiles)

Monaco tiles are committed in test_data/monaco_tiles/ — no extra setup needed.

France tiles

France tiles are used for benchmarks and integration tests but are not committed to the repo:

# Generate from OSM data (requires Docker)
make tiles

This runs the Valhalla tile builder on a France OSM extract and writes tiles to test_data/france_tiles/.

Custom regions

To generate tiles for any region:

  1. Download an OSM extract (.osm.pbf) from Geofabrik
  2. Run the Valhalla tile builder:
    docker run -v /path/to/data:/data ghcr.io/valhalla/valhalla:latest \
      valhalla_build_tiles -c /data/valhalla.json /data/extract.osm.pbf
    
  3. Point Calculon at the output directory: calculon --tile-dir /path/to/tiles

Rustdoc

Generate API documentation for all crates:

cargo doc --workspace --no-deps --open

All public types, traits, and functions are documented. Tile format structs include binary layout comments.