|
// 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.CodeAnalysis;
using System.Reflection;
namespace Aspire.Hosting.Ats;
/// <summary>
/// Contains all scanned types, capabilities, and metadata from ATS assembly scanning.
/// </summary>
/// <remarks>
/// <para>
/// The ATS type system has three distinct categories of types, each with different
/// serialization and code generation behavior:
/// </para>
/// <list type="bullet">
/// <item>
/// <term>Handle Types (<see cref="HandleTypes"/>)</term>
/// <description>
/// Types marked with [AspireExport]. These are passed by reference using opaque handles.
/// The TypeScript SDK generates wrapper classes that hold handles and proxy method calls
/// back to .NET. Examples: IDistributedApplicationBuilder, ContainerResource, EndpointReference.
/// </description>
/// </item>
/// <item>
/// <term>DTO Types (<see cref="DtoTypes"/>)</term>
/// <description>
/// Types marked with [AspireDto]. These are serialized as JSON objects and passed by value.
/// The TypeScript SDK generates interfaces matching the DTO's properties.
/// Examples: CreateBuilderOptions, ResourceSnapshot.
/// </description>
/// </item>
/// <item>
/// <term>Enum Types (<see cref="EnumTypes"/>)</term>
/// <description>
/// Enum types discovered in capability signatures. These are serialized as strings.
/// The TypeScript SDK generates TypeScript enums with string values.
/// </description>
/// </item>
/// </list>
/// </remarks>
[Experimental("ASPIREATS001")]
public sealed class AtsContext
{
private HashSet<Type>? _dtoTypes;
private HashSet<Type>? _handleTypes;
/// <summary>
/// Gets the capabilities discovered during scanning.
/// Capabilities are methods or properties marked with [AspireExport] that can be invoked via RPC.
/// </summary>
public required IReadOnlyList<AtsCapabilityInfo> Capabilities { get; init; }
/// <summary>
/// Gets the handle types discovered during scanning.
/// These are types marked with [AspireExport] that are passed by reference using opaque handles.
/// Code generators create wrapper classes for these types.
/// </summary>
public required IReadOnlyList<AtsTypeInfo> HandleTypes { get; init; }
/// <summary>
/// Gets the DTO types discovered during scanning.
/// These are types marked with [AspireDto] that are serialized as JSON objects.
/// Code generators create interfaces for these types.
/// </summary>
public required IReadOnlyList<AtsDtoTypeInfo> DtoTypes { get; init; }
/// <summary>
/// Gets the enum types discovered during scanning.
/// These are enum types found in capability signatures, serialized as strings.
/// Code generators create enum definitions for these types.
/// </summary>
public required IReadOnlyList<AtsEnumTypeInfo> EnumTypes { get; init; }
/// <summary>
/// Gets any diagnostics (warnings/errors) generated during scanning.
/// </summary>
public IReadOnlyList<AtsDiagnostic> Diagnostics { get; init; } = [];
/// <summary>
/// Runtime registry mapping capability IDs to methods.
/// Internal - only used by dispatcher, not part of the serializable model.
/// </summary>
internal Dictionary<string, MethodInfo> Methods { get; } = new();
/// <summary>
/// Runtime registry mapping capability IDs to properties.
/// Internal - only used by dispatcher, not part of the serializable model.
/// </summary>
internal Dictionary<string, PropertyInfo> Properties { get; } = new();
/// <summary>
/// Gets the type category for a CLR type based on scanned data.
/// Used at runtime for marshalling.
/// </summary>
/// <param name="type">The CLR type to classify.</param>
/// <returns>The type category.</returns>
public AtsTypeCategory GetCategory(Type type)
{
// Check primitives
if (AtsConstants.IsPrimitiveType(type))
{
return AtsTypeCategory.Primitive;
}
// Check enums
if (type.IsEnum)
{
return AtsTypeCategory.Enum;
}
// Check arrays
if (type.IsArray)
{
return AtsTypeCategory.Array;
}
// Check generic collections
if (type.IsGenericType)
{
var genericDef = type.GetGenericTypeDefinition();
if (genericDef == typeof(List<>) || genericDef == typeof(IList<>))
{
return AtsTypeCategory.List;
}
if (genericDef == typeof(IReadOnlyList<>) || genericDef == typeof(IReadOnlyCollection<>))
{
return AtsTypeCategory.Array;
}
if (genericDef == typeof(Dictionary<,>) || genericDef == typeof(IDictionary<,>) ||
genericDef == typeof(IReadOnlyDictionary<,>))
{
return AtsTypeCategory.Dict;
}
}
// Check delegates
if (typeof(Delegate).IsAssignableFrom(type))
{
return AtsTypeCategory.Callback;
}
// Check scanned DTOs
_dtoTypes ??= new HashSet<Type>(DtoTypes.Where(d => d.ClrType != null).Select(d => d.ClrType!));
if (_dtoTypes.Contains(type))
{
return AtsTypeCategory.Dto;
}
// Check scanned handle types ([AspireExport] types passed by reference)
_handleTypes ??= new HashSet<Type>(HandleTypes.Where(t => t.ClrType != null).Select(t => t.ClrType!));
if (_handleTypes.Contains(type))
{
return AtsTypeCategory.Handle;
}
// Fallback to Handle for unknown reference types at runtime
return AtsTypeCategory.Handle;
}
}
|