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 #[serde(default, rename = "endLine", skip_serializing_if = "is_zero")]
201 /// The end line value.
202 pub end_line: usize,
203}
204
205/// One two-type-argument DI service registration a file's invocations
206/// record -- see `extract::RegistrationRecord`.
207///
208/// `service` is the first type argument (the interface), `implementation`
209/// the second (the concrete type). Field order (`service`,
210/// `implementation`, `namespace`, `line`) is significant; neither type name
211/// carries its enclosing-namespace qualification here (that is the
212/// resolver's job, against the registration site's OWN using/alias context,
213/// read from `namespace`).
214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215pub struct FragRegistration {
216 /// The service (first type argument) name.
217 pub service: String,
218 /// The implementation (second type argument) name.
219 pub implementation: String,
220 /// The registration call's enclosing namespace.
221 pub namespace: String,
222 /// The registration call's 1-based line.
223 pub line: usize,
224}
225
226/// One declared type fact: the type NAME, plus its top-level
227/// type-argument descriptors when the declaration carried any. Field order
228/// (`type`, `args`) is significant, and `args` is omitted when absent.
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230pub struct FragFact {
231 #[serde(rename = "type")]
232 /// The type name value.
233 pub type_name: String,
234 #[serde(default, skip_serializing_if = "Option::is_none")]
235 /// The args value.
236 pub args: Option<Vec<String>>,
237}
238
239/// One `extensionMethods` entry. Serialized field order (`name`, `thisType`,
240/// `arityMin`, `arityMax`, `thisArgs`) is significant, and serde emits struct
241/// fields in declaration order. The two arity halves are NOT optional: they joined the
242/// schema with the v6 cache rename, so no fragment this reader can meet is
243/// missing them. `arity_max` is signed because -1 is the unbounded-`params`
244/// sentinel.
245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
246pub struct FragExtensionMethod {
247 /// The name value.
248 pub name: String,
249 #[serde(rename = "thisType")]
250 /// The this type value.
251 pub this_type: String,
252 #[serde(rename = "arityMin")]
253 /// The arity min value.
254 pub arity_min: usize,
255 #[serde(rename = "arityMax")]
256 /// The arity max value.
257 pub arity_max: i64,
258 /// Present only when the this-parameter type is generic (the key is omitted
259 /// key otherwise), so a non-generic entry keeps four fields exactly.
260 #[serde(default, rename = "thisArgs", skip_serializing_if = "Option::is_none")]
261 pub this_args: Option<Vec<String>>,
262}
263
264/// One untyped lambda parameter's callee slot (see extract.rs's `LambdaSlot`).
265///
266/// Field order (`owner`, `member`, `argCount`, `argIndex`,
267/// `arity`, `index`) is significant: serde emits struct fields in
268/// declaration order under `#[serde(rename_all = "camelCase")]`.
269#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
270#[serde(rename_all = "camelCase")]
271pub struct FragLambdaSlot {
272 /// The callee's type-name text.
273 pub owner: String,
274 /// The callee method name.
275 pub member: String,
276 /// The argument count of the callee invocation.
277 pub arg_count: usize,
278 /// The position of the lambda among the callee invocation's arguments.
279 pub arg_index: usize,
280 /// The lambda's own parameter count.
281 pub arity: usize,
282 /// This parameter's position inside the lambda's parameter list.
283 pub index: usize,
284}
285
286#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
287#[serde(untagged)]
288/// Represents `FragUsing`.
289pub enum FragUsing {
290 /// The value value.
291 Alias {
292 /// The alias name.
293 alias: String,
294 /// The aliased target.
295 target: String,
296 /// Whether the directive is global.
297 global: bool,
298 },
299 /// The value value.
300 Plain {
301 /// The imported namespace text.
302 text: String,
303 /// Whether the directive is global.
304 global: bool,
305 },
306}
307
308#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
309/// Represents `FragRef`.
310pub struct FragRef {
311 /// The kind value.
312 pub kind: String,
313 /// The name value.
314 pub name: String,
315 #[serde(default, skip_serializing_if = "Option::is_none")]
316 /// The qualified value.
317 pub qualified: Option<String>,
318 #[serde(default, skip_serializing_if = "Option::is_none")]
319 /// The member value.
320 pub member: Option<String>,
321 /// The line value.
322 pub line: usize,
323 /// Always serialized, including when `null` (imports refs) -- NOT
324 /// `skip_serializing_if`, unlike `qualified`/`member` which are omitted
325 /// entirely when absent. See extract.rs's RefRecord doc comment.
326 pub namespace: Option<String>,
327 #[serde(
328 default,
329 rename = "typeArgCount",
330 skip_serializing_if = "Option::is_none"
331 )]
332 /// The type arg count value.
333 pub type_arg_count: Option<usize>,
334 /// Type-certainty flag (see extract.rs's RefRecord). Serialized last and
335 /// only when `true`; an absent key reads back as false, which also makes
336 /// an older fragment JSON parse safely.
337 #[serde(default, skip_serializing_if = "is_false")]
338 pub generic: bool,
339 /// Receiver fact (see extract.rs's RefRecord). Appended AFTER `generic`,
340 /// and set only when a fact actually fired -- so it serializes last and
341 /// only when present, and an absent key reads back as "no fact", the safe
342 /// default.
343 #[serde(
344 default,
345 rename = "receiverType",
346 skip_serializing_if = "Option::is_none"
347 )]
348 pub receiver_type: Option<String>,
349 /// The callee arg count (see extract.rs's RefRecord). Appended AFTER
350 /// `receiverType`, set only when the member access was the callee of an
351 /// invocation -- so it serializes last and only when present, and an
352 /// absent key reads back as "not a call", which is what keeps a property
353 /// read out of the extension tier.
354 #[serde(default, rename = "argCount", skip_serializing_if = "Option::is_none")]
355 pub arg_count: Option<usize>,
356 /// Receiver generic-arg descriptors (see extract.rs's RefRecord). Appended
357 /// LAST, after `argCount`, set only when the receiver's DECLARED type was
358 /// generic -- an absent key reads back as "not generic", which is what
359 /// makes a generic-vs-non-generic pairing fail to unify.
360 #[serde(
361 default,
362 rename = "receiverArgs",
363 skip_serializing_if = "Option::is_none"
364 )]
365 pub receiver_args: Option<Vec<String>>,
366 /// Enclosing-type stack (see extract.rs's RefRecord). Appended LAST, after
367 /// `receiverArgs`, and set only when non-empty. A Vec rather than an Option
368 /// because empty and absent mean the same thing here -- a ref at namespace
369 /// level and an older cached fragment both read back as "no enclosing
370 /// type", which is what keeps them off the step.
371 #[serde(default, rename = "outerTypes", skip_serializing_if = "Vec::is_empty")]
372 pub outer_types: Vec<String>,
373 /// Generic-arg descriptors for a 'ctor-param' ref (see extract.rs's
374 /// RefRecord). Appended LAST of all, after `outerTypes`, and set only when
375 /// the parameter's type was generic.
376 #[serde(default, skip_serializing_if = "Option::is_none")]
377 pub args: Option<Vec<String>>,
378 /// (See extract.rs's RefRecord.) The type whose PROPERTY the
379 /// qualifier's last segment is, for a two-segment chain whose head the
380 /// enclosing scope could type. Appended after `args`, and never present
381 /// alongside `receiverType`.
382 #[serde(
383 default,
384 rename = "receiverPropertyOwner",
385 skip_serializing_if = "Option::is_none"
386 )]
387 pub receiver_property_owner: Option<String>,
388 /// (See extract.rs's RefRecord.) The type whose METHOD a
389 /// `var x = Q.M(...)` initializer called, and that method's name. Appended
390 /// LAST of all, always as a pair, and never alongside `receiverType`: an
391 /// absent pair reads back as "no call fact", which is what leaves the local
392 /// taken-but-unknown.
393 #[serde(
394 default,
395 rename = "receiverCallOwner",
396 skip_serializing_if = "Option::is_none"
397 )]
398 pub receiver_call_owner: Option<String>,
399 #[serde(
400 default,
401 rename = "receiverCallMember",
402 skip_serializing_if = "Option::is_none"
403 )]
404 /// The receiver call member value.
405 pub receiver_call_member: Option<String>,
406 /// `true` for a `base.` qualifier -- the member lookup starts at the
407 /// enclosing type's bases and never considers the enclosing type
408 /// itself (see `extract.rs`'s `RefRecord`). Appended LAST of all, and
409 /// omitted when `false` -- an absent key reads back as `false`, the
410 /// same as a plain `this.` receiver and as every ref kind that never
411 /// sets it, and also what makes a v15 cached fragment parse safely.
412 #[serde(default, rename = "receiverBase", skip_serializing_if = "is_false")]
413 pub receiver_base: bool,
414 /// `true` when `receiverCallOwner`/`receiverCallMember` came from an
415 /// AWAITED call (see `extract.rs`'s `RefRecord`). Appended LAST of all,
416 /// after `receiverBase`, and omitted when `false` -- an absent key reads
417 /// back as `false`, the same as an unawaited call fact and as every ref
418 /// kind that never sets it, which is also what lets a cached fragment
419 /// from before this field existed parse safely with no cache-version
420 /// bump: it simply reads back as "not awaited", the same answer the
421 /// resolver gave before this field existed.
422 #[serde(default, rename = "receiverAwaited", skip_serializing_if = "is_false")]
423 pub receiver_awaited: bool,
424 /// `true` when this ref's qualifier is a bare identifier for which the
425 /// enclosing MEMBER's own fact table (locals, parameters, lambda
426 /// parameters, patterns, `out` designations) holds ANY entry for the
427 /// name, typed or taken-but-unknown (see `extract.rs`'s `RefRecord` and
428 /// `Scope::has_local_fact`). Read ONLY by the bare-identifier
429 /// field/property fallback: a member-scoped name always shadows a
430 /// same-named field, whether or not anything vouches for its type, so
431 /// that fallback never runs when this is `true`. `false` for every
432 /// dotted or generic qualifier, for `this.`/`base.` (never asked of the
433 /// enclosing scope's local table at all), and for every ref kind but
434 /// `uses-member`. Appended LAST of all, after `receiverAwaited`, and
435 /// omitted when `false` -- an absent key reads back as `false`, the
436 /// same as every ref kind that never sets it, which is also what lets a
437 /// cached fragment from before this field existed parse safely with no
438 /// cache-version bump.
439 #[serde(default, rename = "receiverLocal", skip_serializing_if = "is_false")]
440 pub receiver_local: bool,
441 /// Set when this ref's qualifier is an untyped lambda parameter whose
442 /// type is a delegate parameter of some OTHER callee (see extract.rs's
443 /// `RefRecord`). Never present alongside `receiverType` or
444 /// `receiverCallOwner`. Appended LAST of all, after `receiverLocal`,
445 /// omitted when absent. Joined the schema with the v18 cache bump: a
446 /// v17 fragment read back carries none, and every untyped-lambda
447 /// callee-slot lookup this field backs would silently see no
448 /// candidate.
449 #[serde(
450 default,
451 rename = "receiverLambda",
452 skip_serializing_if = "Option::is_none"
453 )]
454 pub receiver_lambda: Option<FragLambdaSlot>,
455 /// `true` when `receiverType` came off a declaration whose own type
456 /// node carried a `?` (see extract.rs's `RefRecord`). Appended LAST of
457 /// all, after `receiverLambda`, and omitted when `false` -- an absent
458 /// key reads back as `false`, the same as a non-nullable receiver and
459 /// as every ref kind that never sets it. Joined the schema with the
460 /// v20 cache bump: a v19 fragment read back carries none, so a
461 /// `T?` receiver's own `Nullable<T>.Value`/`HasValue`/
462 /// `GetValueOrDefault` unwrap would silently keep resolving against
463 /// `T`'s own same-named member.
464 #[serde(default, rename = "receiverNullable", skip_serializing_if = "is_false")]
465 pub receiver_nullable: bool,
466 /// The parameter count of each delegate-shaped argument of the
467 /// invocation this ref is the callee of -- a lambda literal, or a
468 /// local function passed as a method group (see extract.rs's
469 /// `RefRecord`). Appended LAST of all, after `receiverNullable`,
470 /// omitted when absent. Joined the schema with the v20 cache bump: a
471 /// v19 fragment read back carries none, and a candidate whose delegate
472 /// parameter shape the call's own argument cannot fill would silently
473 /// keep earning a precise edge.
474 #[serde(
475 default,
476 rename = "lambdaArgArity",
477 skip_serializing_if = "Option::is_none"
478 )]
479 pub lambda_arg_arity: Option<Vec<Option<usize>>>,
480}
481
482pub(crate) fn is_false(b: &bool) -> bool {
483 !*b
484}
485
486pub(crate) fn is_zero(n: &usize) -> bool {
487 *n == 0
488}
489
490/// One declared member, with the line its own NAME token sits on.
491/// Field order (`name`, `kind`, `line`, `owner`) is significant, and `owner`
492/// is omitted when empty -- which is how
493/// a markup or resource key, owned by no C# type, serializes with three fields.
494#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
495pub struct FragName {
496 /// The name value.
497 pub name: String,
498 /// The kind value.
499 pub kind: String,
500 /// The line value.
501 pub line: usize,
502 #[serde(default, skip_serializing_if = "String::is_empty")]
503 /// The owner value.
504 pub owner: String,
505}
506
507#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
508/// Represents `Fragment`.
509pub struct Fragment {
510 /// The defs value.
511 pub defs: Vec<FragDef>,
512 /// The usings value.
513 pub usings: Vec<FragUsing>,
514 /// The refs value.
515 pub refs: Vec<FragRef>,
516 /// Appended LAST, after `refs`. Always serialized, like its
517 /// three siblings: a fragment's top-level arrays are a fixed shape, and
518 /// only the fields INSIDE a record follow the omit-when-empty rule.
519 #[serde(default)]
520 pub names: Vec<FragName>,
521 /// The file's two-type-argument DI service registrations -- see
522 /// `FragRegistration`. Appended LAST, after `names`, and omitted when
523 /// empty (unlike its four siblings above, which pre-date the
524 /// omit-when-empty convention): a file recording none serializes exactly
525 /// as it did before this field existed.
526 #[serde(default, skip_serializing_if = "Vec::is_empty")]
527 pub registrations: Vec<FragRegistration>,
528}
529
530/// The two shapes a cached fragment can have. Each rel is keyed to whichever
531/// shape the file's grammar produced; the `ts: 1` tag is what tells them
532/// apart, on disk and at the
533/// resolver's door. Untagged, and TS FIRST: a C#/markup fragment carries no
534/// `ts` key (so the TS arm always fails on it) and a TS fragment carries no
535/// `usings` (so the C# arm always fails on it) -- the discrimination is total
536/// in both directions, never a first-match-wins guess.
537#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
538#[serde(untagged)]
539pub enum AnyFragment {
540 /// Represents `Ts`.
541 Ts(extract::TsFragment),
542 /// Represents `Cs`.
543 Cs(Fragment),
544}
545
546impl From<Fragment> for AnyFragment {
547 fn from(f: Fragment) -> Self {
548 AnyFragment::Cs(f)
549 }
550}
551
552impl From<extract::TsFragment> for AnyFragment {
553 fn from(f: extract::TsFragment) -> Self {
554 AnyFragment::Ts(f)
555 }
556}