Skip to main content

devscout_rs/
query.rs

1// refs/impact query support: enum-member union, `impact_walk`, and
2// personalized PageRank. A `tests` block below pins the behavioral contract
3// with fixture cases exercising every code path.
4//
5// Design notes:
6//
7// - **Two-phase index build.** The index cannot be built in one call that
8//   also reads the graph and then hands back a struct borrowing that graph
9//   -- the result would be self-referential. So the caller reads the graph
10//   first with `graph::read_graph(root)`, which returns `None` when there is
11//   no graph, letting the caller check the `Option` before proceeding, then
12//   calls `load_graph_index(&graph, root)` here, which owns the manifest
13//   join and the index build.
14//
15// - **Manifest corrupt-JSON fail-open.** A corrupt manifest.json surfaces as
16//   `Err(ManifestError::InvalidJson)` from `manifest::read_manifest`.
17//   Panicking through a query path for an operator-error edge case (a
18//   hand-corrupted manifest) is worse than failing open, so
19//   `load_graph_index` treats `Err(_)` the same as "no manifest" -- the same
20//   fail-open-on-parse-error convention `graph::read_graph` follows. A
21//   manifest present but missing its `entries` key is handled the same way.
22//
23// - **Insertion-order-preserving scratch structures.** Several of
24//   `impact_walk`'s scratch maps/sets are NOT just membership/lookup
25//   structures -- their insertion order is directly observable in the final
26//   result: `Hit::symbols` feeds `top_symbols`'s first-3 truncation, and
27//   `visited` key order feeds the `nodes` array handed to
28//   `personalized_page_rank`, whose accumulation loop is float-addition over
29//   that exact order -- not associative, so a different iteration order can
30//   produce a bit-different (though not wrong) result.
31//   `std::collections::{HashMap,HashSet}` make no iteration-order guarantee,
32//   so this module has two tiny private order-preserving helpers,
33//   `SeqSet`/`SeqMap` (a `Vec` plus an index `HashMap`, first insertion wins
34//   the slot). They are pure algorithm scratch state, not artifact structs;
35//   `graph::Def`/`Edge`/`Graph` are reused directly from `graph.rs`.
36//
37// - **`str::cmp` ordering.** Location sorts use plain Unicode-codepoint
38//   `str::cmp` rather than locale-aware collation, the same accepted
39//   trade-off documented at `resolve.rs`'s `capped_candidates`: the two
40//   orderings coincide for every path/id shape this crate actually produces,
41//   and diverge only in a pathological mix of leading case or a
42//   `+`/`.`-adjacent tie -- flagged, not solved, no new ICU dependency.
43//
44// - **Result models.** `RefsModel`/`ImpactModel` (and their row/table
45//   sub-types) are the typed surface `render.rs` consumes. Field names are
46//   `snake_case` (e.g. `model.inbound.uses_type`). Nothing here is
47//   `Serialize` -- `--json` output byte-shape is `cli.rs`'s job, not this
48//   module's.
49//
50// Split by verb, plus the shared substrate the verbs sit on: `seq` (the
51// order-preserving scratch structures), `index` (`GraphIndex` and its
52// loading, plus `def_files`/`def_sites`/`symbol_refs`), `symbol`
53// (`resolve_symbol`'s ladder), and `rank` (personalized PageRank). The verbs
54// themselves: `find`, `refs`/`refs_tables` (the row/table shaping `read`
55// reuses), `read`, `coverage` (test coverage for a symbol -- named to avoid
56// colliding with this module's own `tests`), and `impact` (plus `infra`, the
57// hub-file name-pattern classification `impact` widens against). `dispatch`
58// holds the `implements`/`overrides` plumbing `index.rs` and `refs.rs` both
59// call into (index-build-time adjacency, the bare-member fallback's
60// exact-match half) -- split out to keep those two files under this crate's
61// per-file line budget, not a verb of its own. `occurrence` is the same
62// kind of split: a query-time-only helper that tags two otherwise-identical
63// rows of one inbound/outbound table with a stable position, called from
64// `refs.rs` after ranking/capping and never touching the graph it reads.
65// Every public item keeps the path it had before the split via the
66// `pub use`s below.
67
68mod coverage;
69mod dispatch;
70mod find;
71mod impact;
72mod impact_why;
73mod imported;
74mod index;
75mod infra;
76// `--json` rendering of every query model, moved here from `cli.rs` to keep
77// that file under its size ratchet; `pub(crate)` so `cli.rs` can still call
78// it as `query::json::...`.
79pub(crate) mod json;
80mod member;
81mod occurrence;
82mod outcome;
83mod rank;
84mod read;
85mod refs;
86mod refs_tables;
87mod seq;
88mod symbol;
89mod why;
90
91pub use coverage::{build_tests_model, TestRow, TestVia, TestsModel, TestsResult};
92pub use find::{file_inbound_counts, find_names, first_decl_line_by_file, name_tier, source_line};
93pub use impact::{
94    build_impact_model, impact_walk, looks_like_file_path, resolve_impact_seed, BrakedFile,
95    BrakedIface, ImpactModel, ImpactResult, ImpactRow, ImpactWalkResult, KindLines, SeedKind,
96    SeedResolution, VisitedEntry, DEFAULT_HOPS, DEFAULT_IFACE_MAX_FANIN,
97};
98pub use imported::{build_imported_section, ImportedRow, ImportedSection};
99pub use index::{
100    def_files, def_sites, load_graph_index, load_graph_index_with, symbol_refs, DefSite,
101    GraphIndex, HeuristicEntry, InboundEntry, IndexOptions, OutboundEntry, SymbolRefs,
102};
103pub use infra::{is_infra_file, DEFAULT_HUB_MAX_INDEGREE};
104pub use member::{
105    qualified_member_owners, qualified_seed, resolve_member_seed, MemberCandidate,
106    MemberSeedResolution,
107};
108pub use outcome::{Outcome, SCHEMA_VERSION};
109pub use rank::{personalized_page_rank, DEFAULT_DAMPING, DEFAULT_ITERATIONS};
110pub use read::{build_read_model, ReadModel, ReadResult, ReadSpan};
111pub use refs::{build_refs_model, LineCache, MemberRefEntry, MemberRefs, RefsModel, RefsResult};
112pub use refs_tables::{
113    AmbiguousRow, AmbiguousTables, ImportRow, InboundRow, InboundTables, OutboundRow,
114    OutboundTables, Table, DEFAULT_CAP, INBOUND_CAP, OUTBOUND_CAP, SOURCE_MAX,
115};
116pub use seq::{SeqMap, SeqSet};
117pub use symbol::{resolve_symbol, Resolution};
118pub use why::Why;
119
120#[cfg(test)]
121mod tests;