Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The workspace and its crates

Architecture covers what each crate is responsible for and how they depend on each other. This page covers the workspace itself — the mechanics that hold the four crates together as one project.

The root Cargo.toml is workspace-only

It has no [package] section of its own — only [workspace], [workspace.package], [workspace.dependencies], and the build profiles. Its own header comment explains why (Cargo.toml:1-24): until 5.1.1, the apimock façade’s package metadata lived in this same file, mixing workspace-wide concerns with one crate’s own. 5.1.1 split the façade out into crates/apimock/, leaving this file responsible only for:

  • listing the four workspace members;
  • [workspace.package] — version, edition, MSRV, licence, and the rest of the metadata every crate inherits, so they can’t drift apart;
  • [workspace.dependencies] — every shared external dependency’s version, pinned once;
  • the release/dev build profiles, which only take effect from the workspace root regardless of which member is being built.

Why a façade crate at all

cargo install apimock and npx apimock both need a crate literally named apimock. Splitting the implementation into responsibility crates (apimock-config, apimock-routing, apimock-server) but keeping a thin façade that re-exports them means the install story stays simple — users keep typing the same name they always have, regardless of how the implementation is organised underneath (Cargo.toml:18-24).

Version and dependency pinning

All four crates share one version number (5.15.0 as of this page), one edition (2024), and one MSRV (1.91.0) via version.workspace = true / edition.workspace = true / rust-version.workspace = true in each crate’s own manifest. ./version.sh --update is what changes this (see The quality gates).

External dependencies are pinned once, in [workspace.dependencies] (Cargo.toml:49-81), and each crate’s own [dependencies] pulls the version from there rather than stating its own — so two crates can never end up depending on different versions of the same external crate. The internal crates (apimock-config, apimock-routing, apimock-server) are declared the same way, as path dependencies pinned to the workspace version (Cargo.toml:52-54).

Build profiles

Set once, at the workspace root, because Cargo profile sections only take effect from there:

[profile.release]     # shrink executable size
opt-level       = "z"
lto             = true
strip           = true
codegen-units   = 1

[profile.dev]         # to reasonably improve productivity
opt-level       = 1
lto             = false
incremental     = true

The release profile trades build time for a smaller binary (opt-level = "z", full LTO, stripped symbols, single codegen unit) — sensible for a tool distributed as a downloadable executable. The dev profile does the opposite: light optimisation and incremental compilation, for faster local iteration.