ProxyBaseClasses (#27)

This commit is contained in:
Stef Heyenrath
2022-02-01 18:49:01 +01:00
committed by GitHub
parent 649ed89bb6
commit f9664e0564
45 changed files with 1305 additions and 836 deletions
@@ -1,52 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using ProxyInterfaceSourceGenerator.Enums;
using ProxyInterfaceSourceGenerator.Extensions;
using ProxyInterfaceSourceGenerator.Model;
using ProxyInterfaceSourceGenerator.SyntaxReceiver;
using ProxyInterfaceSourceGenerator.Utils;
namespace ProxyInterfaceSourceGenerator.FileGenerators
namespace ProxyInterfaceSourceGenerator.FileGenerators;
internal class ProxyClassesGenerator : BaseGenerator, IFilesGenerator
{
internal class ProxyClassesGenerator : BaseGenerator, IFilesGenerator
public ProxyClassesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable)
{
public ProxyClassesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable)
}
public IEnumerable<FileData> GenerateFiles()
{
foreach (var ci in Context.CandidateInterfaces)
{
yield return GenerateFile(ci.Value);
}
}
public IEnumerable<FileData> GenerateFiles()
{
foreach (var ci in _context.CandidateInterfaces)
{
yield return GenerateFile(ci.Value);
}
}
private FileData GenerateFile(ProxyData pd)
{
var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName, pd.Usings);
var interfaceName = targetClassSymbol.Symbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.InterfaceName);
var className = targetClassSymbol.Symbol.ResolveProxyClassName();
var constructorName = $"{targetClassSymbol.Symbol.Name}Proxy";
private FileData GenerateFile(ProxyData pd)
{
var targetClassSymbol = GetNamedTypeSymbolByFullName(pd.TypeName, pd.Usings);
var interfaceName = targetClassSymbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.InterfaceName);
var className = targetClassSymbol.ResolveProxyClassName();
var constructorName = $"{targetClassSymbol.Name}Proxy";
var file = new FileData(
$"{targetClassSymbol.Symbol.GetFileName()}Proxy.g.cs",
CreateProxyClassCode(pd.Namespace, targetClassSymbol, pd.ProxyBaseClasses, interfaceName, className, constructorName)
);
var file = new FileData(
$"{targetClassSymbol.GetFileName()}Proxy.g.cs",
CreateProxyClassCode(pd.Namespace, targetClassSymbol, interfaceName, className, constructorName)
);
return file;
}
// _context.GeneratedData.Add(new() { InterfaceName = interfaceName, ClassName = pd.ClassName, FileData = file });
return file;
}
private string CreateProxyClassCode(
string ns,
INamedTypeSymbol targetClassSymbol,
string interfaceName,
string className,
string constructorName) => $@"//----------------------------------------------------------------------------------------
private string CreateProxyClassCode(
string ns,
ClassSymbol targetClassSymbol,
bool proxyBaseClasses,
string interfaceName,
string className,
string constructorName) => $@"//----------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator.
//
@@ -55,7 +52,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
// </auto-generated>
//----------------------------------------------------------------------------------------
{(_supportsNullable ? "#nullable enable" : string.Empty)}
{(SupportsNullable ? "#nullable enable" : string.Empty)}
using System;
using AutoMapper;
@@ -63,13 +60,13 @@ namespace {ns}
{{
public class {className} : {interfaceName}
{{
public {targetClassSymbol} _Instance {{ get; }}
public {targetClassSymbol.Symbol} _Instance {{ get; }}
{GeneratePublicProperties(targetClassSymbol, false)}
{GeneratePublicProperties(targetClassSymbol, proxyBaseClasses)}
{GeneratePublicMethods(targetClassSymbol)}
{GeneratePublicMethods(targetClassSymbol, proxyBaseClasses)}
{GenerateEvents(targetClassSymbol)}
{GenerateEvents(targetClassSymbol, proxyBaseClasses)}
public {constructorName}({targetClassSymbol} instance)
{{
@@ -81,165 +78,164 @@ namespace {ns}
{GeneratePrivateAutoMapper()}
}}
}}
{(_supportsNullable ? "#nullable disable" : string.Empty)}";
private string GeneratePrivateAutoMapper()
{(SupportsNullable ? "#nullable disable" : string.Empty)}";
private string GeneratePrivateAutoMapper()
{
return Context.ReplacedTypes.Count == 0 ? string.Empty : " private readonly IMapper _mapper;";
}
private string GenerateMapperConfigurationForAutoMapper()
{
if (Context.ReplacedTypes.Count == 0)
{
return _context.ReplacedTypes.Count == 0 ? string.Empty : " private readonly IMapper _mapper;";
return string.Empty;
}
private string GenerateMapperConfigurationForAutoMapper()
var str = new StringBuilder();
str.AppendLine(" _mapper = new MapperConfiguration(cfg =>");
str.AppendLine(" {");
foreach (var replacedType in Context.ReplacedTypes)
{
if (_context.ReplacedTypes.Count == 0)
str.AppendLine($" cfg.CreateMap<{replacedType.Key}, {replacedType.Value}>();");
str.AppendLine($" cfg.CreateMap<{replacedType.Value}, {replacedType.Key}>();");
}
str.AppendLine(" }).CreateMapper();");
return str.ToString();
}
private string GeneratePublicProperties(ClassSymbol targetClassSymbol, bool proxyBaseClasses)
{
var str = new StringBuilder();
foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol, proxyBaseClasses))
{
var type = GetPropertyType(property, out var isReplaced);
if (isReplaced)
{
return string.Empty;
str.AppendLine($" public {property.ToPropertyTextForClass(type)}");
}
var str = new StringBuilder();
str.AppendLine(" _mapper = new MapperConfiguration(cfg =>");
str.AppendLine(" {");
foreach (var replacedType in _context.ReplacedTypes)
else
{
str.AppendLine($" cfg.CreateMap<{replacedType.Key}, {replacedType.Value}>();");
str.AppendLine($" cfg.CreateMap<{replacedType.Value}, {replacedType.Key}>();");
str.AppendLine($" public {property.ToPropertyTextForClass()}");
}
str.AppendLine(" }).CreateMapper();");
return str.ToString();
str.AppendLine();
}
private string GeneratePublicProperties(INamedTypeSymbol targetClassSymbol, bool proxyAll)
{
var str = new StringBuilder();
return str.ToString();
}
foreach (var property in MemberHelper.GetPublicProperties(targetClassSymbol))
private string GeneratePublicMethods(ClassSymbol targetClassSymbol, bool proxyBaseClasses)
{
var str = new StringBuilder();
foreach (var method in MemberHelper.GetPublicMethods(targetClassSymbol, proxyBaseClasses))
{
var methodParameters = new List<string>();
var invokeParameters = new List<string>();
foreach (var ps in method.Parameters)
{
var type = GetPropertyType(property, out var isReplaced);
if (isReplaced)
var type = GetParameterType(ps, out _);
methodParameters.Add($"{ps.GetParamsPrefix()}{ps.GetRefPrefix()}{type} {ps.GetSanitizedName()}{ps.GetDefaultValue()}");
invokeParameters.Add($"{ps.GetRefPrefix()}{ps.GetSanitizedName()}_");
}
string returnTypeAsString = GetReplacedType(method.ReturnType, out var returnIsReplaced);
str.AppendLine($" public {returnTypeAsString} {method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", methodParameters)}){method.GetWhereStatement()}");
str.AppendLine(" {");
foreach (var ps in method.Parameters)
{
string normalOrMap = $" = {ps.GetSanitizedName()}";
if (ps.RefKind == RefKind.Out)
{
str.AppendLine($" public {property.ToPropertyTextForClass(type)}");
normalOrMap = string.Empty;
}
else
{
str.AppendLine($" public {property.ToPropertyTextForClass()}");
var type = GetParameterType(ps, out var isReplaced);
if (isReplaced)
{
normalOrMap = $" = _mapper.Map<{ps.Type}>({ps.GetSanitizedName()})";
}
}
str.AppendLine();
str.AppendLine($" {ps.Type} {ps.GetSanitizedName()}_{normalOrMap};");
}
return str.ToString();
}
private string GeneratePublicMethods(INamedTypeSymbol targetClassSymbol)
{
var str = new StringBuilder();
foreach (var method in MemberHelper.GetPublicMethods(targetClassSymbol))
{
var methodParameters = new List<string>();
var invokeParameters = new List<string>();
foreach (var ps in method.Parameters)
{
var type = GetParameterType(ps, out _);
methodParameters.Add($"{ps.GetParamsPrefix()}{ps.GetRefPrefix()}{type} {ps.GetSanitizedName()}{ps.GetDefaultValue()}");
invokeParameters.Add($"{ps.GetRefPrefix()}{ps.GetSanitizedName()}_");
}
string returnTypeAsString = GetReplacedType(method.ReturnType, out var returnIsReplaced);
str.AppendLine($" public {returnTypeAsString} {method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", methodParameters)}){method.GetWhereStatement()}");
str.AppendLine(" {");
foreach (var ps in method.Parameters)
{
string normalOrMap = $" = {ps.GetSanitizedName()}";
if (ps.RefKind == RefKind.Out)
{
normalOrMap = string.Empty;
}
else
{
var type = GetParameterType(ps, out var isReplaced);
if (isReplaced)
{
normalOrMap = $" = _mapper.Map<{ps.Type}>({ps.GetSanitizedName()})";
}
}
str.AppendLine($" {ps.Type} {ps.GetSanitizedName()}_{normalOrMap};");
}
#pragma warning disable RS1024 // Compare symbols correctly
int hash = method.ReturnType.GetHashCode();
int hash = method.ReturnType.GetHashCode();
#pragma warning restore RS1024 // Compare symbols correctly
var alternateReturnVariableName = $"result_{Math.Abs(hash)}";
var alternateReturnVariableName = $"result_{Math.Abs(hash)}";
if (returnTypeAsString == "void")
if (returnTypeAsString == "void")
{
str.AppendLine($" _Instance.{method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", invokeParameters)});");
}
else
{
str.AppendLine($" var {alternateReturnVariableName} = _Instance.{method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", invokeParameters)});");
}
foreach (var ps in method.Parameters.Where(p => p.RefKind == RefKind.Out))
{
string normalOrMap = $" = {ps.GetSanitizedName()}_";
if (ps.GetTypeEnum() == TypeEnum.Complex)
{
str.AppendLine($" _Instance.{method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", invokeParameters)});");
var type = GetParameterType(ps, out var isReplaced);
if (isReplaced)
{
normalOrMap = $" = _mapper.Map<{type}>({ps.GetSanitizedName()}_)";
}
}
str.AppendLine($" {ps.GetSanitizedName()}{normalOrMap};");
}
if (returnTypeAsString != "void")
{
if (returnIsReplaced)
{
str.AppendLine($" return _mapper.Map<{returnTypeAsString}>({alternateReturnVariableName});");
}
else
{
str.AppendLine($" var {alternateReturnVariableName} = _Instance.{method.GetMethodNameWithOptionalTypeParameters()}({string.Join(", ", invokeParameters)});");
str.AppendLine($" return {alternateReturnVariableName};");
}
foreach (var ps in method.Parameters.Where(p => p.RefKind == RefKind.Out))
{
string normalOrMap = $" = {ps.GetSanitizedName()}_";
if (ps.GetTypeEnum() == TypeEnum.Complex)
{
var type = GetParameterType(ps, out var isReplaced);
if (isReplaced)
{
normalOrMap = $" = _mapper.Map<{type}>({ps.GetSanitizedName()}_)";
}
}
str.AppendLine($" {ps.GetSanitizedName()}{normalOrMap};");
}
if (returnTypeAsString != "void")
{
if (returnIsReplaced)
{
str.AppendLine($" return _mapper.Map<{returnTypeAsString}>({alternateReturnVariableName});");
}
else
{
str.AppendLine($" return {alternateReturnVariableName};");
}
}
str.AppendLine(" }");
str.AppendLine();
}
return str.ToString();
str.AppendLine(" }");
str.AppendLine();
}
private string GenerateEvents(INamedTypeSymbol targetClassSymbol)
return str.ToString();
}
private string GenerateEvents(ClassSymbol targetClassSymbol, bool proxyBaseClasses)
{
var str = new StringBuilder();
foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol, proxyBaseClasses))
{
var str = new StringBuilder();
foreach (var @event in MemberHelper.GetPublicEvents(targetClassSymbol))
var name = @event.Key.GetSanitizedName();
var ps = @event.First().Parameters.First();
var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString();
str.Append($" public event {type} {name} {{");
if (@event.Any(e => e.MethodKind == MethodKind.EventAdd))
{
var name = @event.Key.GetSanitizedName();
var ps = @event.First().Parameters.First();
var type = ps.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(ps, out _) : ps.Type.ToString();
str.Append($" public event {type} {name} {{");
if (@event.Any(e => e.MethodKind == MethodKind.EventAdd))
{
str.Append($" add {{ _Instance.{name} += value; }}");
}
if (@event.Any(e => e.MethodKind == MethodKind.EventRemove))
{
str.Append($" remove {{ _Instance.{name} -= value; }}");
}
str.AppendLine(" }");
str.AppendLine();
str.Append($" add {{ _Instance.{name} += value; }}");
}
if (@event.Any(e => e.MethodKind == MethodKind.EventRemove))
{
str.Append($" remove {{ _Instance.{name} -= value; }}");
}
return str.ToString();
str.AppendLine(" }");
str.AppendLine();
}
return str.ToString();
}
}