From 65a79bf1a451aa21e2dcf52fa2117f4efe95d9e8 Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 14 Aug 2025 19:58:57 +0100 Subject: [PATCH 1/5] Establish workspace with core, data, and cli crates --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 9 ++++++++- cli/Cargo.toml | 6 ++++++ cli/src/main.rs | 2 ++ core/Cargo.toml | 6 ++++++ core/src/lib.rs | 1 + data/Cargo.toml | 6 ++++++ data/src/lib.rs | 1 + docs/roadmap.md | 9 ++++----- 9 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 cli/Cargo.toml create mode 100644 cli/src/main.rs create mode 100644 core/Cargo.toml create mode 100644 core/src/lib.rs create mode 100644 data/Cargo.toml create mode 100644 data/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index cdb3a15..ed87843 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "cli" +version = "0.1.0" + +[[package]] +name = "core" +version = "0.1.0" + +[[package]] +name = "data" +version = "0.1.0" + [[package]] name = "wildside-engine" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6671176..8072295 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,10 @@ +[workspace] +members = [ + "core", + "data", + "cli", +] + [package] name = "wildside-engine" version = "0.1.0" @@ -5,7 +12,7 @@ edition = "2024" [dependencies] -[lints.clippy] +[workspace.lints.clippy] pedantic = { level = "warn", priority = -1 } # 1. hygiene diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..16d2db2 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cli" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 0000000..865899c --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,2 @@ +/// Entrypoint for the command-line interface. +fn main() {} diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..143436c --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "core" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 0000000..eb7caff --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1 @@ +//! Core domain types for the Wildside engine. diff --git a/data/Cargo.toml b/data/Cargo.toml new file mode 100644 index 0000000..899331e --- /dev/null +++ b/data/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "data" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/data/src/lib.rs b/data/src/lib.rs new file mode 100644 index 0000000..10c98a2 --- /dev/null +++ b/data/src/lib.rs @@ -0,0 +1 @@ +//! Data access and ingestion logic for the Wildside engine. diff --git a/docs/roadmap.md b/docs/roadmap.md index 35b155e..c53ee59 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -7,10 +7,10 @@ core data structures of the engine. - **Set up Project Structure** -- [ ] Run `cargo new --lib wildside-engine` to create the workspace root. -- [ ] Within the `wildside-engine` directory, create the initial crates: +- [x] Run `cargo new --lib wildside-engine` to create the workspace root. +- [x] Within the `wildside-engine` directory, create the initial crates: `cargo new --lib core`, `cargo new --lib data`, and `cargo new --bin cli`. -- [ ] Configure the root `Cargo.toml` to define the workspace members (`core`, +- [x] Configure the root `Cargo.toml` to define the workspace members (`core`, `data`, `cli`). - **Define Core Domain Model** @@ -26,8 +26,7 @@ core data structures of the engine. `get_pois_in_bbox(&self, bbox: &geo::Rect) -> Vec`. - [ ] Define the `TravelTimeProvider` trait with an `async` method - `get_travel_time_matrix(&self, pois: &[PointOfInterest]) -> Result>, Error>` - . + `get_travel_time_matrix(&self, pois: &[PointOfInterest]) -> Result>, Error>`. - [ ] Define the `Scorer` trait with a `score(&self, poi: &PointOfInterest, profile: &InterestProfile) -> f32` method. From 390d2feb75075e2dc82137da1e3d736d5bda66d7 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 17 Aug 2025 05:14:17 +0100 Subject: [PATCH 2/5] Rename crates and centralise workspace config --- .github/workflows/ci.yml | 36 +++++++++++------------------- Cargo.lock | 6 ++--- Cargo.toml | 13 +++++++---- cli/Cargo.toml | 6 ----- cli/src/main.rs | 2 -- core/Cargo.toml | 6 ----- data/Cargo.toml | 6 ----- data/src/lib.rs | 1 - docs/roadmap.md | 35 +++++++++++++++-------------- wildside-cli/Cargo.toml | 11 +++++++++ wildside-cli/src/main.rs | 2 ++ wildside-core/Cargo.toml | 7 ++++++ {core => wildside-core}/src/lib.rs | 0 wildside-data/Cargo.toml | 7 ++++++ wildside-data/src/lib.rs | 14 ++++++++++++ 15 files changed, 84 insertions(+), 68 deletions(-) delete mode 100644 cli/Cargo.toml delete mode 100644 cli/src/main.rs delete mode 100644 core/Cargo.toml delete mode 100644 data/Cargo.toml delete mode 100644 data/src/lib.rs create mode 100644 wildside-cli/Cargo.toml create mode 100644 wildside-cli/src/main.rs create mode 100644 wildside-core/Cargo.toml rename {core => wildside-core}/src/lib.rs (100%) create mode 100644 wildside-data/Cargo.toml create mode 100644 wildside-data/src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b2520b..a75d3ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,38 +1,28 @@ name: CI on: - pull_request: - branches: [main] push: branches: [main] + pull_request: jobs: - build-test: + build: runs-on: ubuntu-latest - permissions: - contents: read - env: - CARGO_TERM_COLOR: always - BUILD_PROFILE: debug steps: - uses: actions/checkout@v4 - - name: Setup Rust - uses: leynos/shared-actions/.github/actions/setup-rust@v1.1.0 - - name: Format + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2025-06-26 + components: rustfmt, clippy, llvm-tools-preview + override: true + - uses: Swatinem/rust-cache@v2 + - name: Check formatting run: make check-fmt - name: Lint run: make lint - name: Test run: make test - - name: Install cargo-tarpaulin - run: cargo install cargo-tarpaulin - - name: Run coverage - run: cargo tarpaulin --out lcov - - name: Upload coverage data to CodeScene - if: ${{ secrets.CS_ACCESS_TOKEN }} - uses: leynos/shared-actions/.github/actions/upload-codescene-coverage@v1.1.0 - with: - format: lcov - access-token: ${{ secrets.CS_ACCESS_TOKEN }} - installer-checksum: ${{ vars.CODESCENE_CLI_SHA256 }} - + - name: Lint Markdown + run: make markdownlint + - name: Validate diagrams + run: make nixie diff --git a/Cargo.lock b/Cargo.lock index ed87843..87a132a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,15 +3,15 @@ version = 4 [[package]] -name = "cli" +name = "wildside-cli" version = "0.1.0" [[package]] -name = "core" +name = "wildside-core" version = "0.1.0" [[package]] -name = "data" +name = "wildside-data" version = "0.1.0" [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8072295..2a1b43a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,19 @@ [workspace] members = [ - "core", - "data", - "cli", + "wildside-core", + "wildside-data", + "wildside-cli", ] +resolver = "2" + +[workspace.package] +edition = "2024" [package] name = "wildside-engine" version = "0.1.0" -edition = "2024" +edition.workspace = true +publish = false [dependencies] diff --git a/cli/Cargo.toml b/cli/Cargo.toml deleted file mode 100644 index 16d2db2..0000000 --- a/cli/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "cli" -version = "0.1.0" -edition = "2024" - -[dependencies] diff --git a/cli/src/main.rs b/cli/src/main.rs deleted file mode 100644 index 865899c..0000000 --- a/cli/src/main.rs +++ /dev/null @@ -1,2 +0,0 @@ -/// Entrypoint for the command-line interface. -fn main() {} diff --git a/core/Cargo.toml b/core/Cargo.toml deleted file mode 100644 index 143436c..0000000 --- a/core/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "core" -version = "0.1.0" -edition = "2024" - -[dependencies] diff --git a/data/Cargo.toml b/data/Cargo.toml deleted file mode 100644 index 899331e..0000000 --- a/data/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "data" -version = "0.1.0" -edition = "2024" - -[dependencies] diff --git a/data/src/lib.rs b/data/src/lib.rs deleted file mode 100644 index 10c98a2..0000000 --- a/data/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -//! Data access and ingestion logic for the Wildside engine. diff --git a/docs/roadmap.md b/docs/roadmap.md index c53ee59..41a0082 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -15,7 +15,7 @@ core data structures of the engine. - **Define Core Domain Model** -- [ ] In `wildside-engine-core`, define the public struct `PointOfInterest` +- [ ] In `wildside-core`, define the public struct `PointOfInterest` with essential fields like `id`, `location: geo::Coord`, and `tags: HashMap`. - [ ] Define the `InterestProfile` struct to hold a user's selected themes and @@ -26,7 +26,8 @@ core data structures of the engine. `get_pois_in_bbox(&self, bbox: &geo::Rect) -> Vec`. - [ ] Define the `TravelTimeProvider` trait with an `async` method - `get_travel_time_matrix(&self, pois: &[PointOfInterest]) -> Result>, Error>`. + `get_travel_time_matrix(&self, pois: &[PointOfInterest]) -> Result>, Error>` + . - [ ] Define the `Scorer` trait with a `score(&self, poi: &PointOfInterest, profile: &InterestProfile) -> f32` method. @@ -35,7 +36,7 @@ core data structures of the engine. - **Implement OSM PBF Ingestion** -- [ ] In `wildside-engine-data`, add `osmpbf` and `geo` as dependencies. +- [ ] In `wildside-data`, add `osmpbf` and `geo` as dependencies. - [ ] Create a public function `ingest_osm_pbf(path: &Path)` that uses `osmpbf::par_map_reduce` to process a PBF file in parallel. - [ ] Implement the logic to filter for relevant OSM elements (e.g., nodes and @@ -44,7 +45,7 @@ core data structures of the engine. - **Adopt GeoRust Primitives** -- [ ] Standardise on `geo::Coord` for all location data within the +- [ ] Standardize on `geo::Coord` for all location data within the `PointOfInterest` struct. - [ ] Create a function `build_spatial_index(pois: &[PointOfInterest]) -> rstar::RTree` @@ -54,7 +55,7 @@ core data structures of the engine. - **Build Wikidata ETL Pipeline** -- [ ] In `wildside-engine-data`, add `wikidata-rust`, `simd-json`, and +- [ ] In `wildside-data`, add `wikidata-rust`, `simd-json`, and `rusqlite` dependencies. - [ ] Write a script that downloads the latest Wikidata JSON dump. - [ ] Implement a parser that iterates through the dump, filters for entities @@ -65,7 +66,7 @@ core data structures of the engine. - **Develop Initial CLI** -- [ ] In `wildside-engine-cli`, use the `clap` crate to define an `ingest` +- [ ] In `wildside-cli`, use the `clap` crate to define an `ingest` command with arguments for the OSM PBF and Wikidata dump file paths. - [ ] Implement the command's handler to orchestrate the full pipeline: call `ingest_osm_pbf`, then the Wikidata ETL process, and finally @@ -77,11 +78,11 @@ This phase implements the core logic that gives the engine its intelligence. - **Implement Global Popularity Scorer** -- [ ] Create the `wildside-engine-scorer` crate. +- [ ] Create the `wildside-scorer` crate. - [ ] Implement an offline process that iterates through `pois.db`, calculates a popularity score for each POI based on its sitelink count and heritage - status, and normalises the scores. -- [ ] Serialise the resulting `HashMap` of scores to a compact + status, and normalizes the scores. +- [ ] Serialize the resulting `HashMap` of scores to a compact binary file (`popularity.bin`) using a library like `bincode`. - **Implement User Relevance Scorer** @@ -95,7 +96,7 @@ This phase implements the core logic that gives the engine its intelligence. - **Define Stable API** -- [ ] In `wildside-engine-core`, define the `SolveRequest` struct with public +- [ ] In `wildside-core`, define the `SolveRequest` struct with public fields for `start: geo::Coord`, `duration_minutes: u16`, `interests: InterestProfile`, and a `seed: u64` for reproducible results. - [ ] Define the `SolveResponse` struct to include the final `Route`, the total @@ -108,7 +109,7 @@ This phase tackles the complex route-finding algorithm. - **Implement Native VRP Solver** -- [ ] Create the `wildside-engine-solver-vrp` crate with a dependency on +- [ ] Create the `wildside-solver-vrp` crate with a dependency on `vrp-core`. - [ ] Create a `VrpSolver` struct that implements the `Solver` trait from the core crate. @@ -116,7 +117,7 @@ This phase tackles the complex route-finding algorithm. - [ ] It will then fetch the travel time matrix for these candidates from the `TravelTimeProvider`. - [ ] It will configure the `vrp-core` problem and objective function to - maximise the total collected score within the given time budget. + maximize the total collected score within the given time budget. - [ ] Finally, it will run the `vrp-core` solver and transform the result into a `SolveResponse`. @@ -129,9 +130,9 @@ This phase tackles the complex route-finding algorithm. - **Integrate Solver into CLI** -- [ ] Add a `solve` command to `wildside-engine-cli` that accepts a path to a +- [ ] Add a `solve` command to `wildside-cli` that accepts a path to a JSON file. -- [ ] The command will deserialise the JSON into a `SolveRequest`, instantiate +- [ ] The command will deserialize the JSON into a `SolveRequest`, instantiate the necessary components (store, scorer, solver), call the solver, and print the resulting `SolveResponse` as formatted JSON. @@ -157,16 +158,16 @@ This phase ensures the engine is robust, reliable, and ready for integration. - [ ] Use `#[cfg(feature = "...")]` attributes to conditionally compile the different solver and store implementations. -- **Finalise Licensing and Versioning** +- **Finalize Licensing and Versioning** - [ ] Add the ISC `LICENSE` file to the root of the workspace and to each crate's `Cargo.toml`. -- [ ] Initialise a `CHANGELOG.md` file at the root, documenting the initial +- [ ] Initialize a `CHANGELOG.md` file at the root, documenting the initial `0.1.0` feature set. - **(Optional) Implement OR-Tools Solver** -- [ ] Create a `wildside-engine-solver-ortools` crate, conditionally compiled +- [ ] Create a `wildside-solver-ortools` crate, conditionally compiled via the `ortools` feature flag. - [ ] Add a dependency on a suitable OR-Tools wrapper crate. - [ ] Implement the `Solver` trait using the CP-SAT solver, mapping the diff --git a/wildside-cli/Cargo.toml b/wildside-cli/Cargo.toml new file mode 100644 index 0000000..10d1a68 --- /dev/null +++ b/wildside-cli/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wildside-cli" +version = "0.1.0" +edition.workspace = true +publish = false + +[dependencies] + +[[bin]] +name = "wildside" +path = "src/main.rs" diff --git a/wildside-cli/src/main.rs b/wildside-cli/src/main.rs new file mode 100644 index 0000000..042e832 --- /dev/null +++ b/wildside-cli/src/main.rs @@ -0,0 +1,2 @@ +//! Entrypoint for the command-line interface. +fn main() {} diff --git a/wildside-core/Cargo.toml b/wildside-core/Cargo.toml new file mode 100644 index 0000000..c78e1cc --- /dev/null +++ b/wildside-core/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wildside-core" +version = "0.1.0" +edition.workspace = true +publish = false + +[dependencies] diff --git a/core/src/lib.rs b/wildside-core/src/lib.rs similarity index 100% rename from core/src/lib.rs rename to wildside-core/src/lib.rs diff --git a/wildside-data/Cargo.toml b/wildside-data/Cargo.toml new file mode 100644 index 0000000..67c3967 --- /dev/null +++ b/wildside-data/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wildside-data" +version = "0.1.0" +edition.workspace = true +publish = false + +[dependencies] diff --git a/wildside-data/src/lib.rs b/wildside-data/src/lib.rs new file mode 100644 index 0000000..7043b97 --- /dev/null +++ b/wildside-data/src/lib.rs @@ -0,0 +1,14 @@ +//! Data access and ingestion logic for the Wildside engine. +//! +//! Responsibilities: +//! - Define repository and source traits for ingestion and query. +//! - Provide adapters for files, HTTP and databases. +//! - Encapsulate serialisation formats and schema evolution. +//! +//! Boundaries: +//! - Do not encode domain rules (live in `wildside-core`). +//! - Keep blocking I/O off async executors; prefer async-capable clients. +//! +//! Invariants: +//! - Thread-safe by default where feasible. +//! - No global mutable state. From b56fb65f41a4e3aaaf900a2de185e97cd07142b6 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 17 Aug 2025 14:18:43 +0100 Subject: [PATCH 3/5] Pin CI actions and align docs with crate naming --- .github/workflows/ci.yml | 6 ++--- Cargo.lock | 4 --- Cargo.toml | 8 ------ docs/roadmap.md | 21 ++++++++------- docs/wildside-engine-design.md | 48 +++++++++++++++++----------------- 5 files changed, 38 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a75d3ef..96b2393 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,13 +9,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1 with: toolchain: nightly-2025-06-26 components: rustfmt, clippy, llvm-tools-preview override: true - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@7939da402645ba29a2df566723491a2c856e8f8a # v2 - name: Check formatting run: make check-fmt - name: Lint diff --git a/Cargo.lock b/Cargo.lock index 87a132a..9c537d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,3 @@ version = "0.1.0" [[package]] name = "wildside-data" version = "0.1.0" - -[[package]] -name = "wildside-engine" -version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2a1b43a..b4875bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,14 +9,6 @@ resolver = "2" [workspace.package] edition = "2024" -[package] -name = "wildside-engine" -version = "0.1.0" -edition.workspace = true -publish = false - -[dependencies] - [workspace.lints.clippy] pedantic = { level = "warn", priority = -1 } diff --git a/docs/roadmap.md b/docs/roadmap.md index 41a0082..7499c1e 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -9,9 +9,10 @@ core data structures of the engine. - [x] Run `cargo new --lib wildside-engine` to create the workspace root. - [x] Within the `wildside-engine` directory, create the initial crates: - `cargo new --lib core`, `cargo new --lib data`, and `cargo new --bin cli`. -- [x] Configure the root `Cargo.toml` to define the workspace members (`core`, - `data`, `cli`). + `cargo new --lib wildside-core`, `cargo new --lib wildside-data`, and + `cargo new --bin wildside-cli`. +- [x] Configure the root `Cargo.toml` to define the workspace members + (`wildside-core`, `wildside-data`, `wildside-cli`). - **Define Core Domain Model** @@ -45,7 +46,7 @@ core data structures of the engine. - **Adopt GeoRust Primitives** -- [ ] Standardize on `geo::Coord` for all location data within the +- [ ] Standardise on `geo::Coord` for all location data within the `PointOfInterest` struct. - [ ] Create a function `build_spatial_index(pois: &[PointOfInterest]) -> rstar::RTree` @@ -81,8 +82,8 @@ This phase implements the core logic that gives the engine its intelligence. - [ ] Create the `wildside-scorer` crate. - [ ] Implement an offline process that iterates through `pois.db`, calculates a popularity score for each POI based on its sitelink count and heritage - status, and normalizes the scores. -- [ ] Serialize the resulting `HashMap` of scores to a compact + status, and normalises the scores. +- [ ] Serialise the resulting `HashMap` of scores to a compact binary file (`popularity.bin`) using a library like `bincode`. - **Implement User Relevance Scorer** @@ -117,7 +118,7 @@ This phase tackles the complex route-finding algorithm. - [ ] It will then fetch the travel time matrix for these candidates from the `TravelTimeProvider`. - [ ] It will configure the `vrp-core` problem and objective function to - maximize the total collected score within the given time budget. + maximise the total collected score within the given time budget. - [ ] Finally, it will run the `vrp-core` solver and transform the result into a `SolveResponse`. @@ -132,7 +133,7 @@ This phase tackles the complex route-finding algorithm. - [ ] Add a `solve` command to `wildside-cli` that accepts a path to a JSON file. -- [ ] The command will deserialize the JSON into a `SolveRequest`, instantiate +- [ ] The command will deserialise the JSON into a `SolveRequest`, instantiate the necessary components (store, scorer, solver), call the solver, and print the resulting `SolveResponse` as formatted JSON. @@ -158,11 +159,11 @@ This phase ensures the engine is robust, reliable, and ready for integration. - [ ] Use `#[cfg(feature = "...")]` attributes to conditionally compile the different solver and store implementations. -- **Finalize Licensing and Versioning** +- **Finalise Licensing and Versioning** - [ ] Add the ISC `LICENSE` file to the root of the workspace and to each crate's `Cargo.toml`. -- [ ] Initialize a `CHANGELOG.md` file at the root, documenting the initial +- [ ] Initialise a `CHANGELOG.md` file at the root, documenting the initial `0.1.0` feature set. - **(Optional) Implement OR-Tools Solver** diff --git a/docs/wildside-engine-design.md b/docs/wildside-engine-design.md index f1db3db..4bc333c 100644 --- a/docs/wildside-engine-design.md +++ b/docs/wildside-engine-design.md @@ -256,7 +256,7 @@ the core logic. A Rust workspace will be used to manage the engine's components, enforcing clear boundaries while allowing for atomic changes across crates when necessary. -- `wildside-engine-core`: This crate is the heart of the engine. It contains +- `wildside-core`: This crate is the heart of the engine. It contains the pure domain model and traits, with no I/O or specific framework dependencies (i.e., it is `#![no_std]` compatible where possible, without `tokio` or `actix`). @@ -271,7 +271,7 @@ clear boundaries while allowing for atomic changes across crates when necessary. - This crate is deterministic and side-effect free, making it easy to test rigorously with property-based testing and fuzzing. -- `wildside-engine-data`: Contains the ETL logic and data adapters. +- `wildside-data`: Contains the ETL logic and data adapters. - Implements the `PoiStore` trait from the core crate. @@ -279,22 +279,22 @@ clear boundaries while allowing for atomic changes across crates when necessary. building the data artifacts (e.g., SQLite/RocksDB stores and the `rstar` index). -- `wildside-engine-scorer`: Implements the `Scorer` trait. +- `wildside-scorer`: Implements the `Scorer` trait. - Contains the logic for both the offline pre-computation of global popularity scores and the per-request calculation of user relevance. -- `wildside-engine-solver-vrp`: The default, native Rust implementation of the +- `wildside-solver-vrp`: The default, native Rust implementation of the `Solver` trait, using the `vrp-core` library. -- `wildside-engine-solver-ortools`: An optional implementation of the `Solver` +- `wildside-solver-ortools`: An optional implementation of the `Solver` trait, using bindings to Google's CP-SAT solver. This would be enabled via a feature flag for users who require its specific performance characteristics and are willing to manage the C++ dependency. -- `wildside-engine-cli`: A small command-line application for operational tasks. +- `wildside-cli`: A small command-line application for operational tasks. - - `ingest`: Runs the full ETL pipeline from `wildside-engine-data` to build + - `ingest`: Runs the full ETL pipeline from `wildside-data` to build the necessary data artifacts. - `score`: Triggers the batch computation of global popularity scores. @@ -339,7 +339,7 @@ benchmarking, and diagnosing production incidents. A strict separation between offline preparation and online serving is essential for performance and scalability. -- **Offline Path:** The `wildside-engine-cli` is used to execute the idempotent +- **Offline Path:** The `wildside-cli` is used to execute the idempotent ETL process. This process takes raw OSM and Wikidata data and produces a set of optimized, read-only artifacts: @@ -369,8 +369,8 @@ specific implementation a configurable choice. ### 4.1. The `Solver` Trait: A Common Interface -The `wildside-engine-core` crate will define a `Solver` trait. This trait will -have a single primary method, +The `wildside-core` crate will define a `Solver` trait. This trait will have a +single primary method, `solve(request: &SolveRequest) -> Result`, which encapsulates the entire process of finding an optimal route. This abstraction is the key to making the engine flexible and future-proof. @@ -391,14 +391,14 @@ will be configured to maximize the total collected `Score(POI)` from visited powerful built-in metaheuristics will efficiently find a high-quality route within the required few-second timeframe.15 This implementation will live in the -`wildside-engine-solver-vrp` crate. +`wildside-solver-vrp` crate. -### 4.3. Optional High-Performance Backend: `wildside-engine-solver-ortools` +### 4.3. Optional High-Performance Backend: `wildside-solver-ortools` To allow for future performance comparisons or to meet extreme optimization requirements, a second implementation of the `Solver` trait can be provided in -the `wildside-engine-solver-ortools` crate. This would use bindings to Google's -highly optimized CP-SAT solver, such as the `cp_sat` crate. +the `wildside-solver-ortools` crate. This would use bindings to Google's highly +optimized CP-SAT solver, such as the `cp_sat` crate. This approach offers potentially world-class performance but comes at the cost of significant build and deployment complexity, requiring a C++ compiler and a @@ -413,10 +413,10 @@ A critical prerequisite for any VRP solver is the travel time matrix. The solver itself is an abstract mathematical engine; it requires an external component to provide the walking time between every pair of candidate POIs. -This is handled by the `TravelTimeProvider` trait defined in -`wildside-engine-core`. This trait defines the async boundary for the entire -library. While the core solver logic remains synchronous and embeddable, an -implementation of this trait can be asynchronous. +This is handled by the `TravelTimeProvider` trait defined in `wildside-core`. +This trait defines the async boundary for the entire library. While the core +solver logic remains synchronous and embeddable, an implementation of this +trait can be asynchronous. The recommended implementation will be an adapter that makes API calls to an external, open-source routing engine like OSRM or Valhalla, running as a @@ -434,7 +434,7 @@ plan covering packaging, testing, and versioning. The engine will be structured for robust dependency management and deployment. -- **Licensing:** All engine crates (`wildside-engine-*`) will be licensed under +- **Licensing:** All engine crates (`wildside-*`) will be licensed under the permissive **ISC license**, satisfying the project's legal requirements while being clear and concise. @@ -493,14 +493,14 @@ own repository with no code churn, as the boundaries are already established. The migration from an initial "engine-in-app" prototype to the final library structure follows a clear path: -1. Extract all domain types into `wildside-engine-core` and update the +1. Extract all domain types into `wildside-core` and update the application to import from the new crate. -2. Move scoring and solver logic into the `wildside-engine-scorer` and - `wildside-engine-solver-vrp` crates, implementing the traits from `core`. - The application code becomes a thin adapter. +2. Move scoring and solver logic into the `wildside-scorer` and + `wildside-solver-vrp` crates, implementing the traits from `core`. The + application code becomes a thin adapter. -3. Introduce the `wildside-engine-cli` and integrate it into the CI pipeline +3. Introduce the `wildside-cli` and integrate it into the CI pipeline for repeatable data ingestion and performance snapshots. 4. Change the application's dependency on the engine crates from a local From dd3ecfd430780585a75fa7a159265b39cb8e3273 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 17 Aug 2025 18:00:58 +0100 Subject: [PATCH 4/5] Harden workflow and refine docs --- .github/workflows/ci.yml | 7 +++++++ docs/roadmap.md | 21 ++++++++++++++------- docs/wildside-engine-design.md | 13 +++++++------ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96b2393..b401128 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,13 @@ on: branches: [main] pull_request: +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest diff --git a/docs/roadmap.md b/docs/roadmap.md index 7499c1e..9775b10 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -7,12 +7,20 @@ core data structures of the engine. - **Set up Project Structure** -- [x] Run `cargo new --lib wildside-engine` to create the workspace root. -- [x] Within the `wildside-engine` directory, create the initial crates: - `cargo new --lib wildside-core`, `cargo new --lib wildside-data`, and - `cargo new --bin wildside-cli`. +- [x] Create the repository root directory `wildside-engine` and initialise a + virtual workspace: + + ```bash + mkdir wildside-engine && cd wildside-engine + git init + cargo init --vcs git + ``` + +- [x] Replace the root `Cargo.toml` with a virtual workspace manifest (no + `[package]`), defining members: `cargo new --lib wildside-core`, + `cargo new --lib wildside-data`, and `cargo new --bin wildside-cli`. - [x] Configure the root `Cargo.toml` to define the workspace members - (`wildside-core`, `wildside-data`, `wildside-cli`). + (`wildside-core`, `wildside-data`, `wildside-cli`) and set `resolver = "2"`. - **Define Core Domain Model** @@ -27,8 +35,7 @@ core data structures of the engine. `get_pois_in_bbox(&self, bbox: &geo::Rect) -> Vec`. - [ ] Define the `TravelTimeProvider` trait with an `async` method - `get_travel_time_matrix(&self, pois: &[PointOfInterest]) -> Result>, Error>` - . + `get_travel_time_matrix(&self, pois: &[PointOfInterest]) -> Result>, Error>`. - [ ] Define the `Scorer` trait with a `score(&self, poi: &PointOfInterest, profile: &InterestProfile) -> f32` method. diff --git a/docs/wildside-engine-design.md b/docs/wildside-engine-design.md index 4bc333c..884e775 100644 --- a/docs/wildside-engine-design.md +++ b/docs/wildside-engine-design.md @@ -371,9 +371,10 @@ specific implementation a configurable choice. The `wildside-core` crate will define a `Solver` trait. This trait will have a single primary method, -`solve(request: &SolveRequest) -> Result`, which -encapsulates the entire process of finding an optimal route. This abstraction -is the key to making the engine flexible and future-proof. +`solve(request: &SolveRequest) -> Result`, which +encapsulates the entire process of finding an optimal route. The trait is +object-safe and keeps the solver synchronous for embeddability. This +abstraction is the key to making the engine flexible and future-proof. ### 4.2. Recommended Native Rust Solution with `vrp-core` @@ -414,9 +415,9 @@ solver itself is an abstract mathematical engine; it requires an external component to provide the walking time between every pair of candidate POIs. This is handled by the `TravelTimeProvider` trait defined in `wildside-core`. -This trait defines the async boundary for the entire library. While the core -solver logic remains synchronous and embeddable, an implementation of this -trait can be asynchronous. +This trait forms the asynchronous boundary for the library with the signature +`async fn get_travel_time_matrix(...) -> Result<..., core::Error>`. Keeping the +solver synchronous preserves object safety and makes the core embeddable. The recommended implementation will be an adapter that makes API calls to an external, open-source routing engine like OSRM or Valhalla, running as a From c0cc77ffddc1917f3af006c646b67adcc215ce7f Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 18 Aug 2025 00:24:41 +0100 Subject: [PATCH 5/5] Refine toolchain setup and scaffold crates --- .github/workflows/ci.yml | 3 +-- wildside-cli/Cargo.toml | 3 +-- wildside-cli/src/main.rs | 16 ++++++++++++++-- wildside-core/Cargo.toml | 2 -- wildside-data/Cargo.toml | 2 -- wildside-data/src/lib.rs | 2 +- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b401128..90fc1f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,11 +17,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 - - uses: actions-rs/toolchain@63eb9591781c46a70274cb3ebdf190fce92702e8 # v1 + - uses: dtolnay/rust-toolchain@master with: toolchain: nightly-2025-06-26 components: rustfmt, clippy, llvm-tools-preview - override: true - uses: Swatinem/rust-cache@7939da402645ba29a2df566723491a2c856e8f8a # v2 - name: Check formatting run: make check-fmt diff --git a/wildside-cli/Cargo.toml b/wildside-cli/Cargo.toml index 10d1a68..3bd8bf7 100644 --- a/wildside-cli/Cargo.toml +++ b/wildside-cli/Cargo.toml @@ -3,8 +3,7 @@ name = "wildside-cli" version = "0.1.0" edition.workspace = true publish = false - -[dependencies] +default-run = "wildside" [[bin]] name = "wildside" diff --git a/wildside-cli/src/main.rs b/wildside-cli/src/main.rs index 042e832..4d2e22f 100644 --- a/wildside-cli/src/main.rs +++ b/wildside-cli/src/main.rs @@ -1,2 +1,14 @@ -//! Entrypoint for the command-line interface. -fn main() {} +//! Entry point for the command-line interface. +#![forbid(unsafe_code)] + +fn main() { + if let Err(err) = run() { + eprintln!("wildside: {err}"); + std::process::exit(1); + } +} + +fn run() -> Result<(), Box> { + // TODO: parse CLI arguments and dispatch commands. + Ok(()) +} diff --git a/wildside-core/Cargo.toml b/wildside-core/Cargo.toml index c78e1cc..91d9a28 100644 --- a/wildside-core/Cargo.toml +++ b/wildside-core/Cargo.toml @@ -3,5 +3,3 @@ name = "wildside-core" version = "0.1.0" edition.workspace = true publish = false - -[dependencies] diff --git a/wildside-data/Cargo.toml b/wildside-data/Cargo.toml index 67c3967..362402c 100644 --- a/wildside-data/Cargo.toml +++ b/wildside-data/Cargo.toml @@ -3,5 +3,3 @@ name = "wildside-data" version = "0.1.0" edition.workspace = true publish = false - -[dependencies] diff --git a/wildside-data/src/lib.rs b/wildside-data/src/lib.rs index 7043b97..8350c41 100644 --- a/wildside-data/src/lib.rs +++ b/wildside-data/src/lib.rs @@ -3,7 +3,7 @@ //! Responsibilities: //! - Define repository and source traits for ingestion and query. //! - Provide adapters for files, HTTP and databases. -//! - Encapsulate serialisation formats and schema evolution. +//! - Encapsulate serialization formats and schema evolution. //! //! Boundaries: //! - Do not encode domain rules (live in `wildside-core`).