1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Artifact build/load, fragments cache, incremental reuse.
// Writes are atomic (tmp+rename); fragments cache keying is load-bearing --
// a mismatch makes reuse break silently.
//
// This module owns every serde struct for graph.json + the fragments-cache
// pair (fragments-v19.json, fragments-index-v19.json), plus their path resolution,
// atomic I/O, and the cache-then-resolve-then-write orchestration
// (`rebuild_graph`). The pure resolution ladder that
// turns fragments into `defs`/`edges` lives in `resolve.rs` and returns the
// `Graph` value this module serializes -- see that module for the ladder
// itself.
//
// Schema notes (the on-disk format graph.json must keep):
// - graph.json is compact JSON with NO indentation:
// `{"key":value,...}`, no spaces. `serde_json::to_vec` (non-pretty)
// matches this by default.
// - Key order is insertion order, not sorted. Defs keep first-insertion
// order (partial-class duplicates land in
// `also_in`, not a second top-level entry) -- backed here by a
// Vec<Def> + HashMap<id, index> pair (`build_def_index` in resolve.rs)
// rather than a HashMap alone, which has no defined iteration order.
// - `also_in` is omitted entirely (not `[]`) when a def has no additional
// declaring sites -- `#[serde(skip_serializing_if = "Vec::is_empty")]`.
// - `edges` are shape-heterogeneous by `kind`: inherits/uses-type/
// uses-member share `{kind, from_file, from_line, to, to_file}`;
// imports is `{kind, from_file, from_line, target}`; ambiguous is
// `{kind, origin, from_file, from_line, raw, candidates,
// candidate_count}`. Modeled as an internally-tagged enum
// (`#[serde(tag = "kind")]`) -- serde always emits the tag field first,
// matching every one of these shapes' field order.
// - a `uses-member` edge appends three more keys AFTER that shared prefix,
// in this exact order: `heuristic` (omitted when precise), `tier`, then
// `member`. `tier` names WHICH guess tier emitted the row and is present
// exactly when `heuristic` is; `member` names the member the reference
// reads or calls and is written on EVERY uses-member edge, the precise
// ones included -- it is the one fact the row never carried. Both are
// omit-when-`None`, so the append-last rule every other added key follows
// holds here too; the other two flagged kinds (inherits, uses-type) stay
// exactly as they were.
// - `schema_version` is `GRAPH_SCHEMA_VERSION`, which the `tier`/`member`
// append above moved to 2, and which the `implements`/`overrides` edge
// kinds moved to 3. `rebuild_graph`'s unchanged fast path reads
// the first bytes of an existing graph.json and refuses to reuse one
// written at an older version, so a stale artifact is rebuilt on the next
// `map` even when not one fragment moved.
// - `units` is the LAST key of the whole object, appended after `names`
// and omitted entirely (not `[]`) when the repo declares no `.csproj` --
// so a tree without one serializes byte-for-byte as it did before the
// project model existed. Each row's own key order is fixed too: `id`,
// `name`, then `refs` (omitted when the project references nothing) and
// `test` (omitted when false). A unit's DIRECTORY is not persisted: it
// is `id`'s parent, recomputed on read (`project::units_from_graph`),
// and neither is per-file/per-def membership -- that is derived from the
// unit list by `ProjectModel` rather than stored.
// - `stats.edges_by_kind` has a FIXED key order (inherits, uses-type,
// imports, uses-member, ctor-di, implements, overrides) -- not
// alphabetical, not insertion order of first edge seen. A plain struct
// with that declared field order reproduces it.
// - `stats.ambiguous_pct` is `round(x*1000)/10`: a number that prints
// WITHOUT a decimal point when whole (`20`, not `20.0`) and with
// exactly one decimal digit otherwise (`33.3`). Rust's f64 Serialize
// (ryu-backed) does not reproduce this -- see `Percent1` below, which
// stores tenths as an integer and branches serialize_i64/serialize_f64
// so whole values come out as bare integers, without needing serde_json's
// `arbitrary_precision`/`raw_value` features (neither is enabled).
// - fragments.json / fragments-index.json are objects keyed by
// repo-relative path, in file-walk order (fragments-index.json is
// rebuilt by iterating the same cache object fragments.json used).
// A `HashMap` has no serialization order guarantee and `serde_json`'s
// default `Map` (no `preserve_order` feature, which is off here) sorts
// by key -- neither matches. `OrderedMap<V>` below hand-rolls
// Serialize/Deserialize (using serde's own `SerializeMap` / `MapAccess`
// traits, not a bespoke JSON writer) to preserve insertion order.
// - `built_at_head` records `git -C <root> rev-parse HEAD` (`null` on any
// failure incl. no commits yet). `manifest.rs` already owns that
// (`manifest::git_head`) -- `resolve_graph` (resolve.rs) calls THAT, so
// this module does not duplicate it.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;