Fix: Empty Module Paths in Error Catalog

Fixed a bug where all 68 error enums in the generated error catalog had empty "path": "" values, making the catalog useless for locating error types.

Context

The mando errors command generates a JSON error catalog by parsing rustdoc JSON output for three crates: mando_core, mando_lib, and mando_bess. The extractor lives in src/workspace/projects/treats/balazs_mando_error_extractor.rs.

Symptoms

  • Running mando errors produces a JSON catalog where every entry has "path": ""
  • All 68 error enums affected
  • The enum names and variants were extracted correctly; only the module path was missing

Root Cause

The extractor looked up enum item IDs in rustdoc’s doc["paths"] map to resolve the fully-qualified module path. However, rustdoc’s paths map does not include private or pub(crate) items, even when generating docs with --document-private-items. Since most error enums in the Mando workspace are pub(crate), their IDs were absent from paths, and the lookup returned null for every one of them.

Rustdoc Gotcha

--document-private-items causes rustdoc to emit private items into the JSON output (the index map), but it does not add them to the paths map. The paths map only contains items that are publicly reachable. This is an undocumented (or poorly documented) behavior as of Rust 1.88.0.

Fix

Added a fallback path-derivation strategy that activates when the paths lookup fails. The fallback derives the module path from the source file location (span.filename) stored on each item in the rustdoc index.

Two new helpers added to balazs_mando_error_extractor.rs:

  • crate_name_from_root() — extracts the crate name from a --crate-root path
  • derive_module_path() — converts a source file path into a Rust module path:
    1. Strip the crate directory and src/ prefix
    2. Strip the .rs extension
    3. Handle special files: lib.rs and mod.rs map to their parent directory
    4. Convert / separators to ::
    5. Prepend the crate name
    6. Append the enum name

Example derivation:

span.filename: "mando-lib/src/adapter/alpiq/error.rs"
crate root:    "mando-lib/"
→ strip prefix: "adapter/alpiq/error.rs"
→ strip .rs:    "adapter/alpiq/error"
→ convert /:    "adapter::alpiq::error"
→ prepend crate: "mando_lib::adapter::alpiq::error"
→ append enum:   "mando_lib::adapter::alpiq::error::AlpiqError"

Tests

4 new unit tests covering the fallback logic:

  • Standard nested module path derivation
  • lib.rs at crate root
  • mod.rs in a subdirectory
  • Crate name extraction from root path