File: Json\ObjectReaders_TagHelpers.cs
Web Access
Project: ..\..\..\src\RazorSdk\Tool\Microsoft.NET.Sdk.Razor.Tool.csproj (rzc)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Components;
 
namespace Microsoft.NET.Sdk.Razor.Tool.Json;
 
internal static partial class ObjectReaders
{
    public static TagHelperDescriptor ReadTagHelper(JsonDataReader reader)
        => reader.ReadNonNullObject(ReadTagHelperFromProperties);
 
    public static TagHelperDescriptor ReadTagHelperFromProperties(JsonDataReader reader)
    {
        var flags = (TagHelperFlags)reader.ReadByte(nameof(TagHelperDescriptor.Flags));
        var kind = (TagHelperKind)reader.ReadByteOrDefault(nameof(TagHelperDescriptor.Kind), defaultValue: (byte)TagHelperKind.Component);
        var runtimeKind = (RuntimeKind)reader.ReadByteOrDefault(nameof(TagHelperDescriptor.RuntimeKind), defaultValue: (byte)RuntimeKind.IComponent);
        var name = reader.ReadNonNullString(nameof(TagHelperDescriptor.Name));
        var assemblyName = reader.ReadNonNullString(nameof(TagHelperDescriptor.AssemblyName));
 
        var displayName = reader.ReadStringOrNull(nameof(TagHelperDescriptor.DisplayName));
        var typeNameObject = ReadTypeNameObject(reader, nameof(TagHelperDescriptor.TypeName));
        var documentationObject = ReadDocumentationObject(reader, nameof(TagHelperDescriptor.Documentation));
        var tagOutputHint = reader.ReadStringOrNull(nameof(TagHelperDescriptor.TagOutputHint));
 
        var tagMatchingRules = reader.ReadImmutableArrayOrEmpty(nameof(TagHelperDescriptor.TagMatchingRules), ReadTagMatchingRule);
        var boundAttributes = reader.ReadImmutableArrayOrEmpty(nameof(TagHelperDescriptor.BoundAttributes), ReadBoundAttribute);
        var allowedChildTags = reader.ReadImmutableArrayOrEmpty(nameof(TagHelperDescriptor.AllowedChildTags), ReadAllowedChildTag);
 
        var metadata = ReadMetadata(reader, nameof(TagHelperDescriptor.Metadata));
        var diagnostics = reader.ReadImmutableArrayOrEmpty(nameof(TagHelperDescriptor.Diagnostics), ReadDiagnostic);
 
        return new TagHelperDescriptor(
            flags, kind, runtimeKind, name, assemblyName,
            displayName!, typeNameObject, documentationObject, tagOutputHint,
            tagMatchingRules, boundAttributes, allowedChildTags,
            metadata, diagnostics);
 
        static TagMatchingRuleDescriptor ReadTagMatchingRule(JsonDataReader reader)
        {
            return reader.ReadNonNullObject(ReadFromProperties);
 
            static TagMatchingRuleDescriptor ReadFromProperties(JsonDataReader reader)
            {
                var tagName = reader.ReadNonNullString(nameof(TagMatchingRuleDescriptor.TagName));
                var parentTag = reader.ReadStringOrNull(nameof(TagMatchingRuleDescriptor.ParentTag));
                var tagStructure = (TagStructure)reader.ReadInt32OrZero(nameof(TagMatchingRuleDescriptor.TagStructure));
                var caseSensitive = reader.ReadBooleanOrTrue(nameof(TagMatchingRuleDescriptor.CaseSensitive));
                var attributes = reader.ReadImmutableArrayOrEmpty(nameof(TagMatchingRuleDescriptor.Attributes), ReadRequiredAttribute);
 
                var diagnostics = reader.ReadImmutableArrayOrEmpty(nameof(TagMatchingRuleDescriptor.Diagnostics), ReadDiagnostic);
 
                return new TagMatchingRuleDescriptor(
                    tagName, parentTag,
                    tagStructure, caseSensitive,
                    attributes, diagnostics);
            }
        }
 
        static RequiredAttributeDescriptor ReadRequiredAttribute(JsonDataReader reader)
        {
            return reader.ReadNonNullObject(ReadFromProperties);
 
            static RequiredAttributeDescriptor ReadFromProperties(JsonDataReader reader)
            {
                var flags = (RequiredAttributeDescriptorFlags)reader.ReadByte(nameof(RequiredAttributeDescriptor.Flags));
                var name = reader.ReadString(nameof(RequiredAttributeDescriptor.Name));
                var nameComparison = (RequiredAttributeNameComparison)reader.ReadByteOrZero(nameof(RequiredAttributeDescriptor.NameComparison));
                var value = reader.ReadStringOrNull(nameof(RequiredAttributeDescriptor.Value));
                var valueComparison = (RequiredAttributeValueComparison)reader.ReadByteOrZero(nameof(RequiredAttributeDescriptor.ValueComparison));
 
                var diagnostics = reader.ReadImmutableArrayOrEmpty(nameof(RequiredAttributeDescriptor.Diagnostics), ReadDiagnostic);
 
                return new RequiredAttributeDescriptor(
                    flags, name!, nameComparison, value, valueComparison, diagnostics);
            }
        }
 
        static BoundAttributeDescriptor ReadBoundAttribute(JsonDataReader reader)
        {
            return reader.ReadNonNullObject(ReadFromProperties);
 
            static BoundAttributeDescriptor ReadFromProperties(JsonDataReader reader)
            {
                var flags = (BoundAttributeFlags)reader.ReadByte(nameof(BoundAttributeDescriptor.Flags));
                var name = reader.ReadString(nameof(BoundAttributeDescriptor.Name));
                var propertyName = reader.ReadNonNullString(nameof(BoundAttributeDescriptor.PropertyName));
                var typeNameObject = ReadTypeNameObject(reader, nameof(BoundAttributeDescriptor.TypeName));
                var indexerNamePrefix = reader.ReadStringOrNull(nameof(BoundAttributeDescriptor.IndexerNamePrefix));
                var indexerTypeNameObject = ReadTypeNameObject(reader, nameof(BoundAttributeDescriptor.IndexerTypeName));
                var displayName = reader.ReadNonNullString(nameof(BoundAttributeDescriptor.DisplayName));
                var containingType = reader.ReadStringOrNull(nameof(BoundAttributeDescriptor.ContainingType));
                var documentationObject = ReadDocumentationObject(reader, nameof(BoundAttributeDescriptor.Documentation));
                var parameters = reader.ReadImmutableArrayOrEmpty(nameof(BoundAttributeDescriptor.Parameters), ReadBoundAttributeParameter);
                var metadata = ReadMetadata(reader, nameof(BoundAttributeDescriptor.Metadata));
                var diagnostics = reader.ReadImmutableArrayOrEmpty(nameof(BoundAttributeDescriptor.Diagnostics), ReadDiagnostic);
 
                return new BoundAttributeDescriptor(
                    flags, name!, propertyName, typeNameObject,
                    indexerNamePrefix, indexerTypeNameObject,
                    documentationObject, displayName, containingType,
                    parameters, metadata, diagnostics);
            }
        }
 
        static BoundAttributeParameterDescriptor ReadBoundAttributeParameter(JsonDataReader reader)
        {
            return reader.ReadNonNullObject(ReadFromProperties);
 
            static BoundAttributeParameterDescriptor ReadFromProperties(JsonDataReader reader)
            {
                var flags = (BoundAttributeParameterFlags)reader.ReadInt32(nameof(BoundAttributeParameterDescriptor.Flags));
                var name = reader.ReadString(nameof(BoundAttributeParameterDescriptor.Name));
                var propertyName = reader.ReadNonNullString(nameof(BoundAttributeParameterDescriptor.PropertyName));
                var typeNameObject = ReadTypeNameObject(reader, nameof(BoundAttributeParameterDescriptor.TypeName));
                var documentationObject = ReadDocumentationObject(reader, nameof(BoundAttributeParameterDescriptor.Documentation));
                var diagnostics = reader.ReadImmutableArrayOrEmpty(nameof(BoundAttributeParameterDescriptor.Diagnostics), ReadDiagnostic);
 
                return new BoundAttributeParameterDescriptor(
                    flags, name!, propertyName, typeNameObject, documentationObject, diagnostics);
            }
        }
 
        static AllowedChildTagDescriptor ReadAllowedChildTag(JsonDataReader reader)
        {
            return reader.ReadNonNullObject(ReadFromProperties);
 
            static AllowedChildTagDescriptor ReadFromProperties(JsonDataReader reader)
            {
                var name = reader.ReadNonNullString(nameof(AllowedChildTagDescriptor.Name));
                var displayName = reader.ReadNonNullString(nameof(AllowedChildTagDescriptor.DisplayName));
                var diagnostics = reader.ReadImmutableArrayOrEmpty(nameof(AllowedChildTagDescriptor.Diagnostics), ReadDiagnostic);
 
                return new AllowedChildTagDescriptor(name, displayName, diagnostics);
            }
        }
 
        static TypeNameObject ReadTypeNameObject(JsonDataReader reader, string propertyName)
        {
            if (!reader.TryReadPropertyName(propertyName))
            {
                return default;
            }
 
            if (reader.TryReadNull())
            {
                return default;
            }
 
            if (reader.IsInteger)
            {
                var index = reader.ReadByte();
                return new(index);
            }
 
            if (reader.IsObjectStart)
            {
                return reader.ReadNonNullObject(static reader =>
                {
                    var fullName = reader.ReadNonNullString(nameof(TypeNameObject.FullName));
                    var namespaceName = reader.ReadStringOrNull(nameof(TypeNameObject.Namespace));
                    var name = reader.ReadStringOrNull(nameof(TypeNameObject.Name));
 
                    return TypeNameObject.From(fullName, namespaceName, name);
                });
            }
 
            Debug.Assert(reader.IsString);
 
            var fullName = reader.ReadNonNullString();
            return new(fullName);
        }
 
        static DocumentationObject ReadDocumentationObject(JsonDataReader reader, string propertyName)
        {
            return reader.TryReadPropertyName(propertyName)
                ? ReadCore(reader)
                : default;
 
            static DocumentationObject ReadCore(JsonDataReader reader)
            {
                if (reader.IsObjectStart)
                {
                    return reader.ReadNonNullObject(static reader =>
                    {
                        var id = (DocumentationId)reader.ReadInt32(nameof(DocumentationDescriptor.Id));
                        // Check to see if the Args property was actually written before trying to read it;
                        // otherwise, assume the args are null.
                        var args = reader.TryReadPropertyName(nameof(DocumentationDescriptor.Args))
                            ? reader.ReadArray(static r => r.ReadValue())
                            : null;
 
                        if (args is { Length: > 0 and var length })
                        {
                            for (var i = 0; i < length; i++)
                            {
                                if (args[i] is string s)
                                {
                                    args[i] = s;
                                }
                            }
                        }
 
                        return DocumentationDescriptor.From(id, args);
                    });
                }
                else
                {
                    return reader.ReadString() switch
                    {
                        string s => s,
                        null => default(DocumentationObject)
                    };
                }
            }
        }
 
        static MetadataObject ReadMetadata(JsonDataReader reader, string propertyName)
        {
            var metadataKind = (MetadataKind)reader.ReadByteOrDefault(WellKnownPropertyNames.MetadataKind, defaultValue: (byte)MetadataKind.None);
 
            return metadataKind switch
            {
                MetadataKind.None => MetadataObject.None,
                MetadataKind.TypeParameter => reader.ReadNonNullObjectOrDefault(propertyName, ReadTypeParameterMetadata, defaultValue: TypeParameterMetadata.Default),
                MetadataKind.Property => reader.ReadNonNullObjectOrDefault(propertyName, ReadPropertyMetadata, defaultValue: PropertyMetadata.Default),
                MetadataKind.ChildContentParameter => ChildContentParameterMetadata.Default,
                MetadataKind.Bind => reader.ReadNonNullObjectOrDefault(propertyName, ReadBindMetadata, defaultValue: BindMetadata.Default),
                MetadataKind.Component => reader.ReadNonNullObjectOrDefault(propertyName, ReadComponentMetadata, defaultValue: ComponentMetadata.Default),
                MetadataKind.EventHandler => reader.ReadNonNullObject(propertyName, ReadEventHandlerMetadata),
                MetadataKind.ViewComponent => reader.ReadNonNullObject(propertyName, ReadViewComponentMetadata),
                _ => Assumed.Unreachable<MetadataObject>($"Unexpected MetadataKind '{metadataKind}'."),
            };
        }
 
        static TypeParameterMetadata ReadTypeParameterMetadata(JsonDataReader reader)
        {
            var builder = new TypeParameterMetadata.Builder
            {
                IsCascading = reader.ReadBooleanOrFalse(nameof(TypeParameterMetadata.IsCascading)),
                Constraints = reader.ReadStringOrNull(nameof(TypeParameterMetadata.Constraints)),
                NameWithAttributes = reader.ReadStringOrNull(nameof(TypeParameterMetadata.NameWithAttributes))
            };
 
            return builder.Build();
        }
 
        static PropertyMetadata ReadPropertyMetadata(JsonDataReader reader)
        {
            var builder = new PropertyMetadata.Builder
            {
                GloballyQualifiedTypeName = reader.ReadStringOrNull(nameof(PropertyMetadata.GloballyQualifiedTypeName)),
                IsChildContent = reader.ReadBooleanOrFalse(nameof(PropertyMetadata.IsChildContent)),
                IsEventCallback = reader.ReadBooleanOrFalse(nameof(PropertyMetadata.IsEventCallback)),
                IsDelegateSignature = reader.ReadBooleanOrFalse(nameof(PropertyMetadata.IsDelegateSignature)),
                IsDelegateWithAwaitableResult = reader.ReadBooleanOrFalse(nameof(PropertyMetadata.IsDelegateWithAwaitableResult)),
                IsGenericTyped = reader.ReadBooleanOrFalse(nameof(PropertyMetadata.IsGenericTyped)),
                IsInitOnlyProperty = reader.ReadBooleanOrFalse(nameof(PropertyMetadata.IsInitOnlyProperty))
            };
 
            return builder.Build();
        }
 
        static BindMetadata ReadBindMetadata(JsonDataReader reader)
        {
            var builder = new BindMetadata.Builder
            {
                IsFallback = reader.ReadBooleanOrFalse(nameof(BindMetadata.IsFallback)),
                ValueAttribute = reader.ReadStringOrNull(nameof(BindMetadata.ValueAttribute)),
                ChangeAttribute = reader.ReadStringOrNull(nameof(BindMetadata.ChangeAttribute)),
                ExpressionAttribute = reader.ReadStringOrNull(nameof(BindMetadata.ExpressionAttribute)),
                TypeAttribute = reader.ReadStringOrNull(nameof(BindMetadata.TypeAttribute)),
                IsInvariantCulture = reader.ReadBooleanOrFalse(nameof(BindMetadata.IsInvariantCulture)),
                Format = reader.ReadStringOrNull(nameof(BindMetadata.Format))
            };
 
            return builder.Build();
        }
    }
 
    static ComponentMetadata ReadComponentMetadata(JsonDataReader reader)
    {
        var builder = new ComponentMetadata.Builder
        {
            IsGeneric = reader.ReadBooleanOrFalse(nameof(ComponentMetadata.IsGeneric)),
            HasRenderModeDirective = reader.ReadBooleanOrFalse(nameof(ComponentMetadata.HasRenderModeDirective))
        };
 
        return builder.Build();
    }
 
    static EventHandlerMetadata ReadEventHandlerMetadata(JsonDataReader reader)
    {
        var builder = new EventHandlerMetadata.Builder
        {
            EventArgsType = reader.ReadNonNullString(nameof(EventHandlerMetadata.EventArgsType))
        };
 
        return builder.Build();
    }
 
    static ViewComponentMetadata ReadViewComponentMetadata(JsonDataReader reader)
    {
        var builder = new ViewComponentMetadata.Builder
        {
            Name = reader.ReadNonNullString(nameof(ViewComponentMetadata.Name))
        };
 
        return builder.Build();
    }
}