// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
using CommunityToolkit.Mvvm.SourceGenerators.Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
///
/// A model representing an attribute declaration.
///
internal sealed record AttributeInfo(
string TypeName,
ImmutableArray ConstructorArgumentInfo,
ImmutableArray<(string Name, TypedConstantInfo Value)> NamedArgumentInfo)
{
///
/// Creates a new instance from a given value.
///
/// The input value.
/// A instance representing .
public static AttributeInfo From(AttributeData attributeData)
{
string typeName = attributeData.AttributeClass!.GetFullyQualifiedName();
// Get the constructor arguments
ImmutableArray constructorArguments =
attributeData.ConstructorArguments
.Select(TypedConstantInfo.From)
.ToImmutableArray();
// Get the named arguments
ImmutableArray<(string, TypedConstantInfo)>.Builder namedArguments = ImmutableArray.CreateBuilder<(string, TypedConstantInfo)>();
foreach (KeyValuePair arg in attributeData.NamedArguments)
{
namedArguments.Add((arg.Key, TypedConstantInfo.From(arg.Value)));
}
return new(
typeName,
constructorArguments,
namedArguments.ToImmutable());
}
///
/// Gets an instance representing the current value.
///
/// The instance representing the current value.
public AttributeSyntax GetSyntax()
{
// Gather the constructor arguments
IEnumerable arguments =
ConstructorArgumentInfo
.Select(static arg => AttributeArgument(arg.GetSyntax()));
// Gather the named arguments
IEnumerable namedArguments =
NamedArgumentInfo.Select(static arg =>
AttributeArgument(arg.Value.GetSyntax())
.WithNameEquals(NameEquals(IdentifierName(arg.Name))));
return Attribute(IdentifierName(TypeName), AttributeArgumentList(SeparatedList(arguments.Concat(namedArguments))));
}
///
/// An implementation for .
///
public sealed class Comparer : Comparer
{
///
protected override void AddToHashCode(ref HashCode hashCode, AttributeInfo obj)
{
hashCode.Add(obj.TypeName);
hashCode.AddRange(obj.ConstructorArgumentInfo, TypedConstantInfo.Comparer.Default);
foreach ((string key, TypedConstantInfo value) in obj.NamedArgumentInfo)
{
hashCode.Add(key);
hashCode.Add(value, TypedConstantInfo.Comparer.Default);
}
}
///
protected override bool AreEqual(AttributeInfo x, AttributeInfo y)
{
if (x.TypeName != y.TypeName ||
!x.ConstructorArgumentInfo.SequenceEqual(y.ConstructorArgumentInfo, TypedConstantInfo.Comparer.Default) ||
x.NamedArgumentInfo.Length != y.NamedArgumentInfo.Length)
{
return false;
}
for (int i = 0; i < x.NamedArgumentInfo.Length; i++)
{
(string Name, TypedConstantInfo Value) left = x.NamedArgumentInfo[i];
(string Name, TypedConstantInfo Value) right = y.NamedArgumentInfo[i];
if (left.Name != right.Name ||
!TypedConstantInfo.Comparer.Default.Equals(left.Value, right.Value))
{
return false;
}
}
return true;
}
}
}