Skip to main content

devscout_rs/graph/
fragment_types.rs

1use serde::{Deserialize, Serialize};
2
3use crate::extract;
4
5use super::ordered::OrderedMap;
6
7// ---------------------------------------------------------------------------
8// Fragments cache schema -- the RAW (unresolved) per-file extraction output,
9// keyed by repo-relative path. Mirrors extract.rs's DefRecord/UsingRecord/
10// RefRecord field-for-field; the serde impls live here rather than on the
11// extractor's own types.
12// ---------------------------------------------------------------------------
13
14/// Field order is significant: id, name, namespace, kind, line, methods, then
15/// the member-fact additions in that exact order -- properties, fields,
16/// methodReturns -- and after those extensionMethods, appended LAST. Each is
17/// omitted entirely when empty, so a type declaring none of them serializes
18/// exactly as it did before those additions.
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub struct FragDef {
21    /// The id value.
22    pub id: String,
23    /// The name value.
24    pub name: String,
25    /// The namespace value.
26    pub namespace: String,
27    /// The kind value.
28    pub kind: String,
29    /// The line value.
30    pub line: usize,
31    /// The methods value.
32    pub methods: Vec<String>,
33    #[serde(default, skip_serializing_if = "Vec::is_empty")]
34    /// The properties value.
35    pub properties: Vec<String>,
36    #[serde(default, skip_serializing_if = "Vec::is_empty")]
37    /// The fields value.
38    pub fields: Vec<String>,
39    /// Member name -> declared return type NAME, in FIRST-DECLARATION SOURCE
40    /// ORDER -- deliberately `OrderedMap`, never a `BTreeMap`: this is an
41    /// ordered pair list, and sorting the keys here would break the
42    /// serialized bytes.
43    #[serde(
44        default,
45        rename = "methodReturns",
46        skip_serializing_if = "OrderedMap::is_empty"
47    )]
48    pub method_returns: OrderedMap<String>,
49    /// The extension methods this type declares, in source
50    /// order, deduped by (name, thisType, arityMin, arityMax). Appended after
51    /// `method_returns`, and omitted entirely when empty, so a type declaring
52    /// none keeps its exact prior bytes.
53    #[serde(
54        default,
55        rename = "extensionMethods",
56        skip_serializing_if = "Vec::is_empty"
57    )]
58    pub extension_methods: Vec<FragExtensionMethod>,
59    /// DIRECT base-type identifiers, source
60    /// order, deduped. Appended LAST, after `extension_methods`, omitted when
61    /// empty. A resolution input only: `resolve_graph` strips it (along with
62    /// properties/fields/extensionMethods) before graph.json's def rows, so
63    /// the on-disk def bytes are unchanged.
64    #[serde(default, skip_serializing_if = "Vec::is_empty")]
65    pub bases: Vec<String>,
66    /// The declaring type's own type-parameter names, empty for
67    /// every non-generic declaration. Appended after `bases`, omitted when
68    /// empty. A resolution input, like `bases`: the ctor-DI resolver's
69    /// "is this def itself an open-generic implementation" signal.
70    #[serde(default, rename = "typeParams", skip_serializing_if = "Vec::is_empty")]
71    pub type_params: Vec<String>,
72    /// Per base name that carried a type-argument list, that
73    /// list's generic-arg descriptors relative to `type_params` (`OrderedMap`
74    /// for the same reason `method_returns` is one: the serialized key order
75    /// is significant). Appended after `type_params`, omitted when
76    /// empty.
77    #[serde(
78        default,
79        rename = "baseGenericArgs",
80        skip_serializing_if = "OrderedMap::is_empty"
81    )]
82    pub base_generic_args: OrderedMap<Vec<String>>,
83    /// The methods a test framework would DISCOVER as
84    /// tests, source order, deduped. Appended LAST, after `baseGenericArgs`,
85    /// omitted when empty. Unlike its neighbours this one is NOT stripped by
86    /// `resolve_graph`: it is what `devscout tests` answers from.
87    #[serde(default, rename = "testMethods", skip_serializing_if = "Vec::is_empty")]
88    pub test_methods: Vec<String>,
89    /// Property name -> declared type fact, in source order and
90    /// under the same dedup as `properties`. An `OrderedMap` for the same
91    /// reason `method_returns` is one: the serialized key order is significant.
92    /// Appended LAST, after `test_methods`, omitted when empty. A
93    /// resolution input only, like `properties`/`fields`.
94    #[serde(
95        default,
96        rename = "propertyTypes",
97        skip_serializing_if = "OrderedMap::is_empty"
98    )]
99    pub property_types: OrderedMap<FragFact>,
100    /// Field name -> declared type fact, in source order and
101    /// under the same dedup as `fields`. Mirrors `propertyTypes` field for
102    /// field: an `OrderedMap` for the same reason (the serialized key order
103    /// is significant), a resolution input only, like `properties`/`fields`,
104    /// and this is a purely additive field -- an absent key reads back as
105    /// "no fields typed", the safe default for every fragment cached before
106    /// this field existed, so it joins the schema with no cache-version
107    /// bump. Appended right after `propertyTypes`, omitted when empty.
108    #[serde(
109        default,
110        rename = "fieldTypes",
111        skip_serializing_if = "OrderedMap::is_empty"
112    )]
113    pub field_types: OrderedMap<FragFact>,
114    /// Per method name `methodReturns` also carries an entry for,
115    /// that return type's top-level generic-arg descriptors -- the same
116    /// capture `baseGenericArgs` keeps beside `bases` (see extract.rs's
117    /// `DefRecord`). `methodReturns` itself is left exactly as it always
118    /// was (the bare return-type identifier, "Task" for
119    /// `Task<Order> GetAsync()`) so an UNAWAITED use of the same callee
120    /// keeps reading "Task" unchanged; this is a purely additive sibling
121    /// field, not a reshaping of `methodReturns`, which is what lets it
122    /// join the schema with no cache-version bump -- an absent key reads
123    /// back as "no generic args", the safe default for every fragment
124    /// cached before this field existed. Appended LAST of all, after
125    /// `fieldTypes`, omitted when empty.
126    #[serde(
127        default,
128        rename = "methodReturnArgs",
129        skip_serializing_if = "OrderedMap::is_empty"
130    )]
131    pub method_return_args: OrderedMap<Vec<String>>,
132    /// Declared method names `methods` does not carry because
133    /// `is_recorded_method` gates that list on a literal `public` modifier
134    /// (or `kind == "interface"`, where this list is always empty). Source
135    /// order, no dedup, same as `methods`. A resolution input only, like
136    /// `properties`/`fields`/`bases`: `resolve_graph` strips it before
137    /// graph.json's def rows. Consulted only for hierarchy-internal
138    /// receivers (`base.` and the `this.` shape's own base walk); the
139    /// scored tier keeps reading `methods` alone. Appended LAST of all,
140    /// after `methodReturnArgs`, omitted when empty -- purely additive, so
141    /// an absent key reads back as "no non-public methods", the safe
142    /// default for every fragment cached before this field existed, which
143    /// is what lets it join the schema with no cache-version bump.
144    #[serde(
145        default,
146        rename = "nonPublicMethods",
147        skip_serializing_if = "Vec::is_empty"
148    )]
149    pub non_public_methods: Vec<String>,
150    /// Method name -> the (min, max) argument-count range every overload
151    /// sharing that name accepts, one tuple per overload, in declaration
152    /// order -- covers every method, public and non-public alike (see
153    /// `extract::DefRecord::method_arities`). `max` is -1 for an unbounded
154    /// `params` overload, the same sentinel `FragExtensionMethod::arity_max`
155    /// already uses. An `OrderedMap` for the same reason `methodReturns` is
156    /// one: the serialized key order is significant. Appended LAST of all,
157    /// after `nonPublicMethods`, omitted when empty -- purely additive, so
158    /// an absent key reads back as "no arity facts", the safe default for
159    /// every fragment cached before this field existed, which is what lets
160    /// it join the schema with no cache-version bump.
161    #[serde(
162        default,
163        rename = "methodArities",
164        skip_serializing_if = "OrderedMap::is_empty"
165    )]
166    pub method_arities: OrderedMap<Vec<(usize, i64)>>,
167    /// Method name -> per-overload parameter type descriptors, one
168    /// `Vec<String>` per overload in declaration order -- covers every
169    /// method, public and non-public alike, same method set as
170    /// `methodArities` (see `extract::DefRecord::method_params`). A
171    /// position naming a type parameter of the enclosing method or class is
172    /// recorded as `"*"`; a parameter carrying the `this` modifier is
173    /// written `"this <descriptor>"`. A `delegate` def carries exactly one
174    /// entry here, keyed `"Invoke"`. An `OrderedMap` for the same reason
175    /// `methodArities` is one: the serialized key order is significant.
176    /// Appended LAST of all, after `methodArities`, omitted when empty.
177    /// Joined the schema with the v18 cache bump: a v17 fragment read back
178    /// carries none, and every overload-shape/`this`-marker/callee-slot
179    /// lookup this field backs would silently see no candidates.
180    #[serde(
181        default,
182        rename = "methodParams",
183        skip_serializing_if = "OrderedMap::is_empty"
184    )]
185    pub method_params: OrderedMap<Vec<Vec<String>>>,
186    /// Declared method names carrying the literal `override` modifier,
187    /// source order, deduped. Appended LAST of all, after `method_params`,
188    /// omitted when empty -- purely additive, so an absent key reads back
189    /// as "no overrides", the safe default for every fragment cached before
190    /// this field existed, which is what lets it join the schema with the
191    /// same v19 cache bump `registrations` does rather than a bump of its
192    /// own. Read by the resolver's member-level `overrides` pass to find
193    /// the nearest in-graph base member of the same name and arity.
194    #[serde(
195        default,
196        rename = "overrideMethods",
197        skip_serializing_if = "Vec::is_empty"
198    )]
199    pub override_methods: Vec<String>,
200    /// Per base name that carries a NESTED generic type argument, that
201    /// argument's own recursive descriptor -- see
202    /// `extract::DefRecord::base_type_args`. Appended LAST of all, after
203    /// `overrideMethods`, omitted when empty. Joined the schema with the
204    /// v20 cache bump: a v19 fragment read back carries none, so the inner
205    /// type argument of a nested base (`IConsumer<Batch<T>>`'s `T`) would
206    /// be unrecoverable from the flattened `baseGenericArgs` alone.
207    #[serde(
208        default,
209        rename = "baseTypeArgs",
210        skip_serializing_if = "OrderedMap::is_empty"
211    )]
212    pub base_type_args: OrderedMap<Vec<String>>,
213    /// Every distinct single type argument this type's own properties wrap
214    /// -- see `extract::DefRecord::property_message_args`. Appended LAST of
215    /// all, after `baseTypeArgs`, omitted when empty. Joined the schema
216    /// with the v21 cache bump: a v20 fragment read back carries none, so a
217    /// handler binding its messages on properties rather than on its base
218    /// list would look like it handled nothing.
219    #[serde(
220        default,
221        rename = "propertyMessageArgs",
222        skip_serializing_if = "Vec::is_empty"
223    )]
224    pub property_message_args: Vec<String>,
225    /// Base names (a subset of `bases`) whose message-position argument at
226    /// `baseGenericArgs`' own position 0 is an array type
227    /// (`IConsumer<M[]>`) -- see `extract::DefRecord::array_message_bases`.
228    /// Appended LAST of all, after `propertyMessageArgs`, omitted when
229    /// empty. Joined the schema with the v23 cache bump: a v22 fragment
230    /// read back carries none, so a single message and an array of it would
231    /// read as the same base-list argument and a publish of one would
232    /// silently reach a consumer of the other.
233    #[serde(
234        default,
235        rename = "arrayMessageBases",
236        skip_serializing_if = "Vec::is_empty"
237    )]
238    pub array_message_bases: Vec<String>,
239    #[serde(default, rename = "endLine", skip_serializing_if = "is_zero")]
240    /// The end line value.
241    pub end_line: usize,
242}
243
244/// One two-type-argument DI service registration a file's invocations
245/// record -- see `extract::RegistrationRecord`.
246///
247/// `service` is the first type argument (the interface), `implementation`
248/// the second (the concrete type). Field order (`service`,
249/// `implementation`, `namespace`, `line`) is significant; neither type name
250/// carries its enclosing-namespace qualification here (that is the
251/// resolver's job, against the registration site's OWN using/alias context,
252/// read from `namespace`).
253#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
254pub struct FragRegistration {
255    /// The service (first type argument) name.
256    pub service: String,
257    /// The implementation (second type argument) name.
258    pub implementation: String,
259    /// The registration call's enclosing namespace.
260    pub namespace: String,
261    /// The registration call's 1-based line.
262    pub line: usize,
263}
264
265/// One message-bus publish-site fact a file's invocations record -- see
266/// `extract::PublishRecord`. Field order (`verb`, `message`, `namespace`,
267/// `line`, `outerTypes`, `enclosingMethod`, `argCount`, `enclosingCall`) is
268/// significant; `outerTypes` is omitted when empty, `enclosingMethod` when
269/// absent, `argCount` when zero and `enclosingCall` when absent.
270#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
271pub struct FragPublish {
272    /// The invoked method name.
273    pub verb: String,
274    /// The resolved message-type descriptor.
275    pub message: String,
276    /// The call's enclosing namespace.
277    pub namespace: String,
278    /// The call's 1-based line.
279    pub line: usize,
280    /// Enclosing-type stack (see `FragRef::outer_types`).
281    #[serde(default, rename = "outerTypes", skip_serializing_if = "Vec::is_empty")]
282    pub outer_types: Vec<String>,
283    /// The enclosing method's name, set only when this publish hands on its
284    /// own caller's message -- see `extract::PublishRecord::enclosing_method`.
285    /// Appended LAST, omitted when absent. Joined the schema with the v21
286    /// cache bump.
287    #[serde(
288        default,
289        rename = "enclosingMethod",
290        skip_serializing_if = "Option::is_none"
291    )]
292    pub enclosing_method: Option<String>,
293    /// The call's own argument count -- see `extract::PublishRecord::arg_count`.
294    /// Appended LAST, after `enclosingMethod`, omitted when zero. Stays
295    /// within the v22 generation (no accepted or released build has ever
296    /// written v22).
297    #[serde(default, rename = "argCount", skip_serializing_if = "is_zero")]
298    pub arg_count: usize,
299    /// The enclosing invocation the call's own lambda sits inside, when it
300    /// sits inside one -- see `extract::PublishRecord::enclosing_call`.
301    /// Appended LAST, after `argCount`, omitted when absent.
302    #[serde(
303        default,
304        rename = "enclosingCall",
305        skip_serializing_if = "Option::is_none"
306    )]
307    pub enclosing_call: Option<FragEnclosingCall>,
308}
309
310/// One publish call's own enclosing-lambda-argument fact -- see
311/// `extract::EnclosingCallFact`. Field order (`verb`, `argPosition`,
312/// `argCount`) is significant.
313#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
314pub struct FragEnclosingCall {
315    /// The enclosing invocation's own callee bare name.
316    pub verb: String,
317    /// The 0-based position the lambda occupies among the enclosing
318    /// invocation's arguments.
319    #[serde(rename = "argPosition")]
320    pub arg_position: usize,
321    /// The enclosing invocation's own total argument count.
322    #[serde(rename = "argCount")]
323    pub arg_count: usize,
324}
325
326/// One one-type-argument handler registration a file's invocations record
327/// -- see `extract::HandlerRegistrationRecord`. Field order (`handler`,
328/// `namespace`, `line`) is significant.
329#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
330pub struct FragHandlerRegistration {
331    /// The registered type's descriptor, as written.
332    pub handler: String,
333    /// The registration call's enclosing namespace.
334    pub namespace: String,
335    /// The registration call's 1-based line.
336    pub line: usize,
337}
338
339/// One declared type fact: the type NAME, plus its top-level
340/// type-argument descriptors when the declaration carried any. Field order
341/// (`type`, `args`) is significant, and `args` is omitted when absent.
342#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
343pub struct FragFact {
344    #[serde(rename = "type")]
345    /// The type name value.
346    pub type_name: String,
347    #[serde(default, skip_serializing_if = "Option::is_none")]
348    /// The args value.
349    pub args: Option<Vec<String>>,
350}
351
352/// One `extensionMethods` entry. Serialized field order (`name`, `thisType`,
353/// `arityMin`, `arityMax`, `thisArgs`) is significant, and serde emits struct
354/// fields in declaration order. The two arity halves are NOT optional: they joined the
355/// schema with the v6 cache rename, so no fragment this reader can meet is
356/// missing them. `arity_max` is signed because -1 is the unbounded-`params`
357/// sentinel.
358#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
359pub struct FragExtensionMethod {
360    /// The name value.
361    pub name: String,
362    #[serde(rename = "thisType")]
363    /// The this type value.
364    pub this_type: String,
365    #[serde(rename = "arityMin")]
366    /// The arity min value.
367    pub arity_min: usize,
368    #[serde(rename = "arityMax")]
369    /// The arity max value.
370    pub arity_max: i64,
371    /// Present only when the this-parameter type is generic (the key is omitted
372    /// key otherwise), so a non-generic entry keeps four fields exactly.
373    #[serde(default, rename = "thisArgs", skip_serializing_if = "Option::is_none")]
374    pub this_args: Option<Vec<String>>,
375}
376
377/// One untyped lambda parameter's callee slot (see extract.rs's `LambdaSlot`).
378///
379/// Field order (`owner`, `member`, `argCount`, `argIndex`,
380/// `arity`, `index`) is significant: serde emits struct fields in
381/// declaration order under `#[serde(rename_all = "camelCase")]`.
382#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
383#[serde(rename_all = "camelCase")]
384pub struct FragLambdaSlot {
385    /// The callee's type-name text.
386    pub owner: String,
387    /// The callee method name.
388    pub member: String,
389    /// The argument count of the callee invocation.
390    pub arg_count: usize,
391    /// The position of the lambda among the callee invocation's arguments.
392    pub arg_index: usize,
393    /// The lambda's own parameter count.
394    pub arity: usize,
395    /// This parameter's position inside the lambda's parameter list.
396    pub index: usize,
397}
398
399#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
400#[serde(untagged)]
401/// Represents `FragUsing`.
402pub enum FragUsing {
403    /// The value value.
404    Alias {
405        /// The alias name.
406        alias: String,
407        /// The aliased target.
408        target: String,
409        /// Whether the directive is global.
410        global: bool,
411    },
412    /// The value value.
413    Plain {
414        /// The imported namespace text.
415        text: String,
416        /// Whether the directive is global.
417        global: bool,
418    },
419}
420
421#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
422/// Represents `FragRef`.
423pub struct FragRef {
424    /// The kind value.
425    pub kind: String,
426    /// The name value.
427    pub name: String,
428    #[serde(default, skip_serializing_if = "Option::is_none")]
429    /// The qualified value.
430    pub qualified: Option<String>,
431    #[serde(default, skip_serializing_if = "Option::is_none")]
432    /// The member value.
433    pub member: Option<String>,
434    /// The line value.
435    pub line: usize,
436    /// Always serialized, including when `null` (imports refs) -- NOT
437    /// `skip_serializing_if`, unlike `qualified`/`member` which are omitted
438    /// entirely when absent. See extract.rs's `RefRecord` doc comment.
439    pub namespace: Option<String>,
440    #[serde(
441        default,
442        rename = "typeArgCount",
443        skip_serializing_if = "Option::is_none"
444    )]
445    /// The type arg count value.
446    pub type_arg_count: Option<usize>,
447    /// Type-certainty flag (see extract.rs's `RefRecord`). Serialized last and
448    /// only when `true`; an absent key reads back as false, which also makes
449    /// an older fragment JSON parse safely.
450    #[serde(default, skip_serializing_if = "is_false")]
451    pub generic: bool,
452    /// Receiver fact (see extract.rs's `RefRecord`). Appended AFTER `generic`,
453    /// and set only when a fact actually fired -- so it serializes last and
454    /// only when present, and an absent key reads back as "no fact", the safe
455    /// default.
456    #[serde(
457        default,
458        rename = "receiverType",
459        skip_serializing_if = "Option::is_none"
460    )]
461    pub receiver_type: Option<String>,
462    /// The callee arg count (see extract.rs's `RefRecord`). Appended AFTER
463    /// `receiverType`, set only when the member access was the callee of an
464    /// invocation -- so it serializes last and only when present, and an
465    /// absent key reads back as "not a call", which is what keeps a property
466    /// read out of the extension tier.
467    #[serde(default, rename = "argCount", skip_serializing_if = "Option::is_none")]
468    pub arg_count: Option<usize>,
469    /// Receiver generic-arg descriptors (see extract.rs's `RefRecord`). Appended
470    /// LAST, after `argCount`, set only when the receiver's DECLARED type was
471    /// generic -- an absent key reads back as "not generic", which is what
472    /// makes a generic-vs-non-generic pairing fail to unify.
473    #[serde(
474        default,
475        rename = "receiverArgs",
476        skip_serializing_if = "Option::is_none"
477    )]
478    pub receiver_args: Option<Vec<String>>,
479    /// Enclosing-type stack (see extract.rs's `RefRecord`). Appended LAST, after
480    /// `receiverArgs`, and set only when non-empty. A Vec rather than an Option
481    /// because empty and absent mean the same thing here -- a ref at namespace
482    /// level and an older cached fragment both read back as "no enclosing
483    /// type", which is what keeps them off the step.
484    #[serde(default, rename = "outerTypes", skip_serializing_if = "Vec::is_empty")]
485    pub outer_types: Vec<String>,
486    /// Generic-arg descriptors for a 'ctor-param' ref (see extract.rs's
487    /// `RefRecord`). Appended LAST of all, after `outerTypes`, and set only when
488    /// the parameter's type was generic.
489    #[serde(default, skip_serializing_if = "Option::is_none")]
490    pub args: Option<Vec<String>>,
491    /// (See extract.rs's `RefRecord`.) The type whose PROPERTY the
492    /// qualifier's last segment is, for a two-segment chain whose head the
493    /// enclosing scope could type. Appended after `args`, and never present
494    /// alongside `receiverType`.
495    #[serde(
496        default,
497        rename = "receiverPropertyOwner",
498        skip_serializing_if = "Option::is_none"
499    )]
500    pub receiver_property_owner: Option<String>,
501    /// (See extract.rs's `RefRecord`.) The type whose METHOD a
502    /// `var x = Q.M(...)` initializer called, and that method's name. Appended
503    /// LAST of all, always as a pair, and never alongside `receiverType`: an
504    /// absent pair reads back as "no call fact", which is what leaves the local
505    /// taken-but-unknown.
506    #[serde(
507        default,
508        rename = "receiverCallOwner",
509        skip_serializing_if = "Option::is_none"
510    )]
511    pub receiver_call_owner: Option<String>,
512    #[serde(
513        default,
514        rename = "receiverCallMember",
515        skip_serializing_if = "Option::is_none"
516    )]
517    /// The receiver call member value.
518    pub receiver_call_member: Option<String>,
519    /// `true` for a `base.` qualifier -- the member lookup starts at the
520    /// enclosing type's bases and never considers the enclosing type
521    /// itself (see `extract.rs`'s `RefRecord`). Appended LAST of all, and
522    /// omitted when `false` -- an absent key reads back as `false`, the
523    /// same as a plain `this.` receiver and as every ref kind that never
524    /// sets it, and also what makes a v15 cached fragment parse safely.
525    #[serde(default, rename = "receiverBase", skip_serializing_if = "is_false")]
526    pub receiver_base: bool,
527    /// `true` when `receiverCallOwner`/`receiverCallMember` came from an
528    /// AWAITED call (see `extract.rs`'s `RefRecord`). Appended LAST of all,
529    /// after `receiverBase`, and omitted when `false` -- an absent key reads
530    /// back as `false`, the same as an unawaited call fact and as every ref
531    /// kind that never sets it, which is also what lets a cached fragment
532    /// from before this field existed parse safely with no cache-version
533    /// bump: it simply reads back as "not awaited", the same answer the
534    /// resolver gave before this field existed.
535    #[serde(default, rename = "receiverAwaited", skip_serializing_if = "is_false")]
536    pub receiver_awaited: bool,
537    /// `true` when this ref's qualifier is a bare identifier for which the
538    /// enclosing MEMBER's own fact table (locals, parameters, lambda
539    /// parameters, patterns, `out` designations) holds ANY entry for the
540    /// name, typed or taken-but-unknown (see `extract.rs`'s `RefRecord` and
541    /// `Scope::has_local_fact`). Read ONLY by the bare-identifier
542    /// field/property fallback: a member-scoped name always shadows a
543    /// same-named field, whether or not anything vouches for its type, so
544    /// that fallback never runs when this is `true`. `false` for every
545    /// dotted or generic qualifier, for `this.`/`base.` (never asked of the
546    /// enclosing scope's local table at all), and for every ref kind but
547    /// `uses-member`. Appended LAST of all, after `receiverAwaited`, and
548    /// omitted when `false` -- an absent key reads back as `false`, the
549    /// same as every ref kind that never sets it, which is also what lets a
550    /// cached fragment from before this field existed parse safely with no
551    /// cache-version bump.
552    #[serde(default, rename = "receiverLocal", skip_serializing_if = "is_false")]
553    pub receiver_local: bool,
554    /// Set when this ref's qualifier is an untyped lambda parameter whose
555    /// type is a delegate parameter of some OTHER callee (see extract.rs's
556    /// `RefRecord`). Never present alongside `receiverType` or
557    /// `receiverCallOwner`. Appended LAST of all, after `receiverLocal`,
558    /// omitted when absent. Joined the schema with the v18 cache bump: a
559    /// v17 fragment read back carries none, and every untyped-lambda
560    /// callee-slot lookup this field backs would silently see no
561    /// candidate.
562    #[serde(
563        default,
564        rename = "receiverLambda",
565        skip_serializing_if = "Option::is_none"
566    )]
567    pub receiver_lambda: Option<FragLambdaSlot>,
568    /// `true` when `receiverType` came off a declaration whose own type
569    /// node carried a `?` (see extract.rs's `RefRecord`). Appended LAST of
570    /// all, after `receiverLambda`, and omitted when `false` -- an absent
571    /// key reads back as `false`, the same as a non-nullable receiver and
572    /// as every ref kind that never sets it. Joined the schema with the
573    /// v20 cache bump: a v19 fragment read back carries none, so a
574    /// `T?` receiver's own `Nullable<T>.Value`/`HasValue`/
575    /// `GetValueOrDefault` unwrap would silently keep resolving against
576    /// `T`'s own same-named member.
577    #[serde(default, rename = "receiverNullable", skip_serializing_if = "is_false")]
578    pub receiver_nullable: bool,
579    /// The parameter count of each delegate-shaped argument of the
580    /// invocation this ref is the callee of -- a lambda literal, or a
581    /// local function passed as a method group (see extract.rs's
582    /// `RefRecord`). Appended LAST of all, after `receiverNullable`,
583    /// omitted when absent. Joined the schema with the v20 cache bump: a
584    /// v19 fragment read back carries none, and a candidate whose delegate
585    /// parameter shape the call's own argument cannot fill would silently
586    /// keep earning a precise edge.
587    #[serde(
588        default,
589        rename = "lambdaArgArity",
590        skip_serializing_if = "Option::is_none"
591    )]
592    pub lambda_arg_arity: Option<Vec<Option<usize>>>,
593}
594
595pub(crate) fn is_false(b: &bool) -> bool {
596    !*b
597}
598
599pub(crate) fn is_zero(n: &usize) -> bool {
600    *n == 0
601}
602
603/// One declared member, with the line its own NAME token sits on.
604///
605/// Field order (`name`, `kind`, `line`, `owner`) is significant, and `owner`
606/// is omitted when empty -- which is how
607/// a markup or resource key, owned by no C# type, serializes with three fields.
608#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
609pub struct FragName {
610    /// The name value.
611    pub name: String,
612    /// The kind value.
613    pub kind: String,
614    /// The line value.
615    pub line: usize,
616    #[serde(default, skip_serializing_if = "String::is_empty")]
617    /// The owner value.
618    pub owner: String,
619}
620
621#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
622/// Represents `Fragment`.
623pub struct Fragment {
624    /// The defs value.
625    pub defs: Vec<FragDef>,
626    /// The usings value.
627    pub usings: Vec<FragUsing>,
628    /// The refs value.
629    pub refs: Vec<FragRef>,
630    /// Appended LAST, after `refs`. Always serialized, like its
631    /// three siblings: a fragment's top-level arrays are a fixed shape, and
632    /// only the fields INSIDE a record follow the omit-when-empty rule.
633    #[serde(default)]
634    pub names: Vec<FragName>,
635    /// The file's two-type-argument DI service registrations -- see
636    /// `FragRegistration`. Appended LAST, after `names`, and omitted when
637    /// empty (unlike its four siblings above, which pre-date the
638    /// omit-when-empty convention): a file recording none serializes exactly
639    /// as it did before this field existed.
640    #[serde(default, skip_serializing_if = "Vec::is_empty")]
641    pub registrations: Vec<FragRegistration>,
642    /// The file's message-bus publish-site facts -- see `FragPublish`.
643    /// Appended LAST, after `registrations`, and omitted when empty.
644    /// Joined the schema with the v20 cache bump: a v19 fragment read back
645    /// carries none, so every publish-site fact from an unchanged file
646    /// would silently disappear from a reused cache.
647    #[serde(default, skip_serializing_if = "Vec::is_empty")]
648    pub publishes: Vec<FragPublish>,
649    /// The file's one-type-argument handler registrations, appended LAST
650    /// after `publishes` and omitted when empty. Joined the schema with the
651    /// v21 cache bump: a v20 fragment read back carries none, so a
652    /// repository's own handler vocabulary would be underivable from a
653    /// reused cache.
654    #[serde(
655        default,
656        rename = "handlerRegistrations",
657        skip_serializing_if = "Vec::is_empty"
658    )]
659    pub handler_registrations: Vec<FragHandlerRegistration>,
660}
661
662/// The two shapes a cached fragment can have. Each rel is keyed to whichever
663/// shape the file's grammar produced; the `ts: 1` tag is what tells them
664/// apart, on disk and at the
665/// resolver's door. Untagged, and TS FIRST: a C#/markup fragment carries no
666/// `ts` key (so the TS arm always fails on it) and a TS fragment carries no
667/// `usings` (so the C# arm always fails on it) -- the discrimination is total
668/// in both directions, never a first-match-wins guess.
669#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
670#[serde(untagged)]
671pub enum AnyFragment {
672    /// Represents `Ts`.
673    Ts(extract::TsFragment),
674    /// Represents `Cs`.
675    Cs(Fragment),
676}
677
678impl From<Fragment> for AnyFragment {
679    fn from(f: Fragment) -> Self {
680        AnyFragment::Cs(f)
681    }
682}
683
684impl From<extract::TsFragment> for AnyFragment {
685    fn from(f: extract::TsFragment) -> Self {
686        AnyFragment::Ts(f)
687    }
688}