| | | 1 | | using System.Diagnostics.CodeAnalysis; |
| | | 2 | | using System.Text.Json.Serialization; |
| | | 3 | | |
| | | 4 | | namespace NGql.Core.Abstractions; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// Represents a GraphQL inline fragment — a type-conditional selection set rendered as |
| | | 8 | | /// <c>... on TypeName { … }</c>. Used to narrow a parent field whose schema type is a |
| | | 9 | | /// union or interface to a specific concrete type. |
| | | 10 | | /// </summary> |
| | | 11 | | /// <remarks> |
| | | 12 | | /// Inline fragments are siblings of regular fields under their parent <see cref="FieldDefinition"/> |
| | | 13 | | /// (see <see cref="FieldDefinition.InlineFragments"/>). They are kept in a separate collection |
| | | 14 | | /// because their access pattern (lookup by type name) and rendering shape (the <c>... on</c> |
| | | 15 | | /// prefix) differ from regular fields. Nested inline fragments — fragments inside fragments — |
| | | 16 | | /// are supported recursively. |
| | | 17 | | /// </remarks> |
| | | 18 | | [SuppressMessage("Minor Code Smell", "S2292:Trivial properties should be auto-implemented")] |
| | | 19 | | public sealed record InlineFragmentDefinition |
| | | 20 | | { |
| | | 21 | | internal FieldChildren? _fields; |
| | | 22 | | internal Dictionary<string, InlineFragmentDefinition>? _fragments; |
| | | 23 | | |
| | | 24 | | /// <summary> |
| | | 25 | | /// Creates an inline fragment for <paramref name="typeName"/> with no children. |
| | | 26 | | /// </summary> |
| | | 27 | | /// <param name="typeName">The GraphQL type the fragment narrows to (e.g. <c>"Repository"</c>). |
| | | 28 | | /// Rendered verbatim — case matters, GraphQL type names are case-sensitive.</param> |
| | 69 | 29 | | public InlineFragmentDefinition(string typeName) |
| | | 30 | | { |
| | 69 | 31 | | if (string.IsNullOrWhiteSpace(typeName)) |
| | | 32 | | { |
| | 9 | 33 | | throw new ArgumentException("Inline fragment type name cannot be null or whitespace.", nameof(typeName)); |
| | | 34 | | } |
| | | 35 | | |
| | 60 | 36 | | TypeName = typeName; |
| | 60 | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <summary> |
| | | 40 | | /// The GraphQL type the fragment narrows to. Rendered verbatim after <c>... on </c>. |
| | | 41 | | /// </summary> |
| | | 42 | | [JsonPropertyName("typeName")] |
| | 114 | 43 | | public string TypeName { get; init; } |
| | | 44 | | |
| | | 45 | | /// <summary> |
| | | 46 | | /// Fields selected when the parent value's runtime type is <see cref="TypeName"/>. |
| | | 47 | | /// </summary> |
| | | 48 | | [JsonPropertyName("fields")] |
| | | 49 | | public IReadOnlyDictionary<string, FieldDefinition> Fields |
| | 15 | 50 | | => (IReadOnlyDictionary<string, FieldDefinition>?)_fields ?? EmptyFields; |
| | | 51 | | |
| | | 52 | | /// <summary> |
| | | 53 | | /// Get-or-add the underlying <see cref="FieldChildren"/> store. Internal so the builder |
| | | 54 | | /// can pass a stable reference when the user's lambda adds fields to the fragment. |
| | | 55 | | /// </summary> |
| | | 56 | | internal FieldChildren GetOrCreateFieldsStore() |
| | 51 | 57 | | => _fields ??= new FieldChildren(); |
| | | 58 | | |
| | | 59 | | /// <summary> |
| | | 60 | | /// Nested inline fragments inside this fragment's selection set. Useful when the fragment's |
| | | 61 | | /// fields themselves return union/interface types that need further narrowing. |
| | | 62 | | /// </summary> |
| | | 63 | | [JsonPropertyName("inlineFragments")] |
| | | 64 | | public IReadOnlyDictionary<string, InlineFragmentDefinition> InlineFragments |
| | 9 | 65 | | => (IReadOnlyDictionary<string, InlineFragmentDefinition>?)_fragments ?? EmptyFragments; |
| | | 66 | | |
| | 3 | 67 | | private static readonly IReadOnlyDictionary<string, FieldDefinition> EmptyFields = new Dictionary<string, FieldDefin |
| | 3 | 68 | | private static readonly IReadOnlyDictionary<string, InlineFragmentDefinition> EmptyFragments = new Dictionary<string |
| | | 69 | | |
| | | 70 | | public bool Equals(InlineFragmentDefinition? other) |
| | | 71 | | { |
| | 6 | 72 | | if (other is null) return false; |
| | 6 | 73 | | if (ReferenceEquals(this, other)) return true; |
| | 6 | 74 | | return string.Equals(TypeName, other.TypeName, StringComparison.Ordinal); |
| | | 75 | | } |
| | | 76 | | |
| | 6 | 77 | | public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(TypeName); |
| | | 78 | | } |