Skip to content

docs(rust): a category index on the crate front page, from the grouping the registry already had (#179 D6) - #288

Merged
mario4tier merged 1 commit into
TA-Lib:devfrom
kevinlincg:issue-179-category-index
Aug 30, 2026
Merged

docs(rust): a category index on the crate front page, from the grouping the registry already had (#179 D6)#288
mario4tier merged 1 commit into
TA-Lib:devfrom
kevinlincg:issue-179-category-index

Conversation

@kevinlincg

Copy link
Copy Markdown
Collaborator

D6: "1020 methods on one Core with no category index, prelude, or grouping. abstract_api::Group already encodes the grouping at runtime but is not surfaced as docs. 'How do I find the candlestick ones?' works only by alphabetical accident."

The alphabetical accident is real but partial. The 61 recognizers do all start with CDL, and nothing else does — so that one question happens to have an answer. It buys nothing for the other nine groups: AD, ADOSC, CMF, EFI, MARKETFI, NVI, OBV, PVI, PVO and VWAP are the volume studies, and their names say so only to a reader who already knows.

What this adds

rust_doc::category_index renders the grouping as //! lines that the lib.rs scaffolding substitutes into the crate docs: one ## heading per group with its member count, and under it one line per function — a link to that function's own page, then that row's hint.

//! ## Volume Indicators (10)
//!
//! * [`AD`](Core::AD) — Chaikin A/D Line
//! * [`ADOSC`](Core::ADOSC) — Chaikin A/D Oscillator
...

Groups come out in the order Group::ALL declares them: a BTreeMap over the display strings, which is the same order, so the front page and the registry enumerate categories identically.

Two fields, both straight off the definition — the name that is also the method's name, and the hint that becomes FuncInfo::hint and the FuncId variant's own doc line. No third source, so the index cannot say something about a function that the registry does not. That includes the empty-hint case: the registry stores "", and the index writes the link alone rather than falling back to description and reading differently from every other surface. No shipped function has an empty hint today; the rule is there so that adding one cannot make the two disagree.

What gates it, and what did not

rustdoc, already. [SMA](Core::SMA) naming a method that does not exist is rustdoc::broken_intra_doc_links, and the nightly's clippy job runs cargo doc under RUSTDOCFLAGS=-D warnings. Control, watched: rewriting one entry to Core::SMAX fails that step —

error: unresolved link to `Core::SMAX`
   --> library/src/lib.rs:179:15
    | //! * [`SMA`](Core::SMAX) — Simple Moving Average
    |               ^^^^^^^^^^ the struct `Core` has no field or associated item named `SMAX`
    = note: `-D rustdoc::broken-intra-doc-links` implied by `-D warnings`

Nothing gated an omission, and that is why the builder lives in rust_doc rather than inline in main.rs: there it is reachable from the test suite. A filter that quietly drops a function leaves a page that still builds clean and simply never mentions it. rust_category_index_lists_every_function_once walks the corpus and requires each name exactly once, no entry beyond the corpus, and each heading's count equal to the bullets under it. Both halves watched red:

sabotage result
builder skips Pattern Recognition CDL2CROWS must appear exactly once in the category index
heading count members.len() + 1 Cycle Indicators says 6 but lists 5

The cost

The front page grows: 108,739 → 137,217 bytes of rendered HTML (+26%), 215 lines of //! in a generated file. That is the whole of it, and it is the maintainer's call whether a longer front page is worth a category index; nothing is special-cased to hide it.

Doc build time is not part of the cost, and I am not claiming it is cheaper either: three warm cargo doc --no-deps runs each way gave 2.88 / 3.04 / 2.91 s before and 2.82 / 2.90 / 2.91 s after. The ranges overlap — the numbers do not discriminate.

Deliberately not done here

Each is a decision rather than a rendering, so each is yours:

  • The prelude D6 also asks for. A public re-export set is API surface, not docs, and it freezes before the first publish.
  • The same index in README.md. Core::SMA links resolve in rustdoc and nowhere else, so crates.io and GitHub would render 176 dead spans.
  • A group label on each FuncId variant. The row knows its group; whether the variant doc should repeat it is a taste call about a page that is already 176 lines long.

Verification

Run here:

  • generator cargo test --no-fail-fast — green; backend_suite 311, one of them new.
  • cargo clippy --all-targets -- -D warnings — clean on the generator and on the generated crate.
  • cargo doc --no-deps -p ta-lib under RUSTDOCFLAGS=-D warnings — clean.
  • the crate's 534 doctests, and its --tests suites — green.
  • generate leaves the tree clean apart from this diff: no C, Java or C# file moves.

NOT run here: ta_regtest and the cross-language servers. Nothing this touches is compiled into one — the diff is a doc comment and the code that writes it.

…A-Lib#179 D6)

D6: "1020 methods on one `Core` with no category index, prelude, or grouping.
`abstract_api::Group` already encodes the grouping at runtime but is not
surfaced as docs. 'How do I find the candlestick ones?' works only by
alphabetical accident."

The alphabetical accident is real but partial: the 61 recognizers do all start
with `CDL`, and nothing else does. It buys nothing for the other nine groups --
AD, ADOSC, CMF, EFI, MARKETFI, NVI, OBV, PVI, PVO and VWAP are the volume
studies, and their names say so only if you already know the answer.

`rust_doc::category_index` renders the grouping the registry has always had, as
`//!` lines the `lib.rs` scaffolding substitutes in: one `##` per group, in the
order `Group::ALL` declares (a `BTreeMap` over the display strings, which is the
same order), each carrying its member count, each member a link to its own
method page followed by that row's `hint`.

Two fields, both straight off the definition: the `name` that is also the
method's name, and the `hint` that becomes `FuncInfo::hint` and the `FuncId`
variant's doc line. No third source, so the index cannot say something about a
function that the registry does not -- including the empty-hint case, where the
registry stores `""` and the index writes the link alone rather than falling
back to `description` and reading differently from every other surface. No
shipped function has an empty hint today; the rule is there so that adding one
cannot make the two disagree.

What gates it, and what did not:

  rustdoc, already. `[`SMA`](Core::SMA)` naming a method that does not exist is
  `rustdoc::broken_intra_doc_links`, and the nightly's clippy job runs
  `cargo doc` under `RUSTDOCFLAGS=-D warnings`. Watched it: rewriting one entry
  to `Core::SMAX` fails that step with "the struct `Core` has no field or
  associated item named `SMAX`", exit 101.

  Nothing gated an omission, which is why the builder is in `rust_doc` rather
  than inline in `main.rs`: there it is reachable from the test suite.
  `rust_category_index_lists_every_function_once` walks the corpus and requires
  each name exactly once, no entry beyond it, and each heading's count equal to
  the bullets under it. Watched both halves: dropping Pattern Recognition from
  the builder fails on CDL2CROWS, and `members.len() + 1` fails with "Cycle
  Indicators says 6 but lists 5".

The cost is the front page: 108,739 -> 137,217 bytes of rendered HTML (+26%),
215 lines of `//!` in a generated file. Doc build time is not part of it --
three warm `cargo doc --no-deps` runs each way, 2.88/3.04/2.91s before and
2.82/2.90/2.91s after, ranges that overlap; the numbers do not discriminate and
I claim no difference either way.

Deliberately not done here, since each is a decision rather than a rendering:
the `prelude` D6 also asks for (a public re-export set is API surface, not
docs), the same index in README.md (`Core::SMA` links resolve in rustdoc and
nowhere else, so crates.io would render 176 dead spans), and a group label on
each `FuncId` variant.

Run here: generator `cargo test --no-fail-fast` green (311 in backend_suite,
one of them new); `cargo clippy --all-targets -- -D warnings` clean on the
generator and on the generated crate; `cargo doc --no-deps` clean under
`-D warnings`; the crate's 534 doctests and its `--tests` suites green;
`generate` leaves the tree clean apart from this diff -- no C, Java or C# file
moves. NOT run here: ta_regtest and the cross-language servers. Nothing this
touches is compiled into one -- the diff is a doc comment and the code that
writes it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants