diff --git a/src-examples/ProxyInterfaceConsumer/IPerson.cs b/src-examples/ProxyInterfaceConsumer/IPerson.cs index af7e469..4c75259 100644 --- a/src-examples/ProxyInterfaceConsumer/IPerson.cs +++ b/src-examples/ProxyInterfaceConsumer/IPerson.cs @@ -1,6 +1,6 @@ namespace SourceGeneratorInterface { - [ProxyInterfaceGenerator.Proxy(typeof(SourceGeneratorInterface.Person), false)] + [ProxyInterfaceGenerator.Proxy(typeof(SourceGeneratorInterface.Person), true)] public partial interface IPerson { } diff --git a/src-examples/ProxyInterfaceConsumer/Program.cs b/src-examples/ProxyInterfaceConsumer/Program.cs index d7576d3..46f2716 100644 --- a/src-examples/ProxyInterfaceConsumer/Program.cs +++ b/src-examples/ProxyInterfaceConsumer/Program.cs @@ -8,8 +8,8 @@ namespace SourceGeneratorInterface { IPerson p = new PersonProxy(new Person()); p.Name = "test"; - p.MyNamedTypeSymbol = null; - p.Compilation = null; + //p.MyNamedTypeSymbol = null; + //p.Compilation = null; //p.Add("x"); //p.Void(); Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(p)); diff --git a/src/ProxyInterfaceSourceGenerator/Context.cs b/src/ProxyInterfaceSourceGenerator/Context.cs index 3f2a249..ab242c1 100644 --- a/src/ProxyInterfaceSourceGenerator/Context.cs +++ b/src/ProxyInterfaceSourceGenerator/Context.cs @@ -1,8 +1,12 @@ -using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; namespace ProxyInterfaceSourceGenerator { - internal record Context(GeneratorExecutionContext GeneratorExecutionContext) + internal record Context { + public GeneratorExecutionContext GeneratorExecutionContext { get; init; } + + public List GeneratedData { get; } = new List(); } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ContextData.cs b/src/ProxyInterfaceSourceGenerator/ContextData.cs new file mode 100644 index 0000000..8fb4220 --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/ContextData.cs @@ -0,0 +1,13 @@ +using ProxyInterfaceSourceGenerator.FileGenerators; + +namespace ProxyInterfaceSourceGenerator +{ + internal record ContextData + { + public string? InterfaceName { get; init; } + + public string? ClassName { get; init; } + + public FileData FileData { get; init; } + } +} \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs b/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs index 60cc630..f0ff14e 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs +++ b/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs @@ -5,18 +5,20 @@ namespace ProxyInterfaceSourceGenerator.Extensions { internal static class SymbolExtensions { - public static string ToPropertyText(this IPropertySymbol property) + public static string ToPropertyText(this IPropertySymbol property, string? overrideType = null) { - string get = property.GetMethod != null ? "get; " : string.Empty; - string set = property.SetMethod != null ? "set; " : string.Empty; + var get = property.GetMethod != null ? "get; " : string.Empty; + var set = property.SetMethod != null ? "set; " : string.Empty; - return $"{property.Type} {property.Name} {{ {get}{set}}}"; + var type = !string.IsNullOrEmpty(overrideType) ? overrideType : $"{property.Type}"; + + return $"{type} {property.Name} {{ {get}{set}}}"; } public static string ToPropertyTextForClass(this IPropertySymbol property) { - string get = property.GetMethod != null ? $"get => _instance.{property.Name}; " : string.Empty; - string set = property.SetMethod != null ? $"set => _instance.{property.Name} = value; " : string.Empty; + var get = property.GetMethod != null ? $"get => _instance.{property.Name}; " : string.Empty; + var set = property.SetMethod != null ? $"set => _instance.{property.Name} = value; " : string.Empty; return $"{property.Type} {property.Name} {{ {get}{set}}}"; } diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs new file mode 100644 index 0000000..933517d --- /dev/null +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using ProxyInterfaceSourceGenerator.SyntaxReceiver; + +namespace ProxyInterfaceSourceGenerator.FileGenerators +{ + internal abstract class BaseGenerator + { + protected readonly Context _context; + protected readonly IDictionary _candidateInterfaces; + + public BaseGenerator(Context context, IDictionary candidateInterfaces) + { + _context = context; + _candidateInterfaces = candidateInterfaces; + } + + protected INamedTypeSymbol GetType(string name) + { + var symbol = _context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(name); + if (symbol is null) + { + throw new Exception($"The type '{name}' is not found."); + } + + return symbol; + } + } +} \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs index ebee7f1..8c1b61f 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs @@ -10,32 +10,38 @@ using ProxyInterfaceSourceGenerator.Utils; namespace ProxyInterfaceSourceGenerator.FileGenerators { - internal class PartialInterfacesGenerator : IFilesGenerator + internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator { - private readonly Context _context; - private readonly IDictionary _candidateInterfaces; + private readonly List files = new List(); - public PartialInterfacesGenerator(Context context, IDictionary candidateInterfaces) + public PartialInterfacesGenerator(Context context, IDictionary candidateInterfaces) : + base(context, candidateInterfaces) { - _context = context; - _candidateInterfaces = candidateInterfaces; } public IEnumerable GenerateFiles() { foreach (var ci in _candidateInterfaces) { - var symbol = _context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(ci.Value.TypeName); - if (symbol is null) - { - throw new Exception($"The type '{ci.Value.TypeName}' is not found."); - } - - yield return new FileData( - $"{ci.Value.InterfaceName}.cs", - CreatePartialInterfaceCode(symbol, ci.Value.InterfaceName, ci.Value.ProxyAll) - ); + var file = GenerateFile(ci.Value.InterfaceName, ci.Value.TypeName, ci.Value.ProxyAll); + files.Add(file); } + + return files; + } + + private FileData GenerateFile(string interfaceName, string typeName, bool proxyAll) + { + var symbol = GetType(typeName); + + var file = new FileData( + $"{interfaceName}.cs", + CreatePartialInterfaceCode(symbol, interfaceName, proxyAll) + ); + + _context.GeneratedData.Add(new() { InterfaceName = interfaceName, ClassName = null, FileData = file }); + + return file; } private string CreatePartialInterfaceCode(INamedTypeSymbol symbol, string interfaceName, bool proxyAll) => $@"using System; @@ -78,14 +84,29 @@ namespace {symbol.ContainingNamespace} p => p.Type.TypeKind != TypeKind.Interface }; - if (proxyAll) - { - - } - foreach (var property in MemberHelper.GetPublicProperties(symbol, complexFilters.ToArray())) { - str.AppendLine($" {property.ToPropertyText()}"); + if (proxyAll) + { + var existing = _context.GeneratedData + .FirstOrDefault(x => x.ClassName == $"{property.Name}Proxy" || x.InterfaceName == $"I{property.Name}"); + + if (existing is not null) + { + str.AppendLine($" {property.ToPropertyText(existing.InterfaceName)}"); + } + else + { + // Create new + var typeName = $"{property.Type}"; + var file = GenerateFile($"I{property.Name}", typeName, false); + str.AppendLine($" // {property.ToPropertyText($"I{property.Name}")}"); + } + } + else + { + str.AppendLine($" {property.ToPropertyText()}"); + } str.AppendLine(); } diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs index a591966..5d606cb 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs @@ -9,26 +9,18 @@ using ProxyInterfaceSourceGenerator.Utils; namespace ProxyInterfaceSourceGenerator.FileGenerators { - internal class ProxyClassesGenerator : IFilesGenerator + internal class ProxyClassesGenerator : BaseGenerator, IFilesGenerator { - private readonly Context _context; - private readonly IDictionary _candidateInterfaces; - - public ProxyClassesGenerator(Context context, IDictionary candidateInterfaces) + public ProxyClassesGenerator(Context context, IDictionary candidateInterfaces) : + base(context, candidateInterfaces) { - _context = context; - _candidateInterfaces = candidateInterfaces; } public IEnumerable GenerateFiles() { foreach (var ci in _candidateInterfaces) { - var symbol = _context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(ci.Value.TypeName); - if (symbol is null) - { - throw new Exception($"The type '{ci.Value.TypeName}' is not found."); - } + var symbol = GetType(ci.Value.TypeName); yield return new FileData( $"{ci.Value.ClassName}Proxy.cs", @@ -44,20 +36,25 @@ namespace {symbol.ContainingNamespace} public class {className}Proxy : {interfaceName} {{ private {className} _instance; -{GenerateComplexFields(symbol,proxyAll)} +{GeneratePrivateComplexInterfaceFields(symbol,proxyAll)} public {className}Proxy({className} instance) {{ _instance = instance; }} -{GenerateProperties(symbol, proxyAll)} +{GeneratePublicProperties(symbol, proxyAll)} -{GenerateMethods(symbol)} +{GeneratePublicMethods(symbol)} }} }}"; - private string GenerateComplexFields(INamedTypeSymbol symbol, bool proxyAll) + private string GeneratePrivateComplexInterfaceFields(INamedTypeSymbol symbol, bool proxyAll) { + if (!proxyAll) + { + return string.Empty; + } + var str = new StringBuilder(); foreach (var property in GetComplexProperties(symbol, proxyAll)) @@ -68,7 +65,7 @@ namespace {symbol.ContainingNamespace} return str.ToString(); } - private string GenerateProperties(INamedTypeSymbol symbol, bool proxyAll) + private string GeneratePublicProperties(INamedTypeSymbol symbol, bool proxyAll) { var str = new StringBuilder(); @@ -115,7 +112,7 @@ namespace {symbol.ContainingNamespace} return MemberHelper.GetPublicProperties(symbol, complexFilters.ToArray()); } - private string GenerateMethods(INamedTypeSymbol symbol) + private string GeneratePublicMethods(INamedTypeSymbol symbol) { var str = new StringBuilder(); foreach (var method in MemberHelper.GetPublicMethods(symbol)) diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs index 1f5a1e2..ec66569 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs @@ -13,17 +13,20 @@ namespace ProxyInterfaceSourceGenerator public void Initialize(GeneratorInitializationContext context) { - //if (!System.Diagnostics.Debugger.IsAttached) - //{ - // System.Diagnostics.Debugger.Launch(); - //} + if (!System.Diagnostics.Debugger.IsAttached) + { + System.Diagnostics.Debugger.Launch(); + } context.RegisterForSyntaxNotifications(() => new ProxySyntaxReceiver()); } public void Execute(GeneratorExecutionContext ctx) { - var context = new Context(ctx); + var context = new Context + { + GeneratorExecutionContext = ctx + }; var attributeData = _proxyAttributeGenerator.GenerateFile(); context.GeneratorExecutionContext.AddSource(attributeData.FileName, SourceText.From(attributeData.Text, Encoding.UTF8));