Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CSharp;
- using Microsoft.CodeAnalysis.CSharp.Syntax;
- using Microsoft.CodeAnalysis.Text;
- using SourceGeneratorsAttributes.FactoryGenerationAttributes;
- using SourceGeneratorsCollection.Extensions;
- using SourceGeneratorsLibrary.Extensions;
- using System.Collections.Generic;
- using System.Collections.Immutable;
- using System.ComponentModel;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- namespace System.Runtime.CompilerServices
- {
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal static class IsExternalInit { }
- }
- namespace Generators.Factory
- {
- [Generator]
- public class AbstractFactoryGenerator : ISourceGenerator
- {
- record Combination(ClassDeclarationSyntax Target, ClassDeclarationSyntax Prototype, ICollection<ITypeSymbol> GenericTypes, string FactoryName);
- static bool GetPartialClasses(ClassDeclarationSyntax x) => x.Modifiers.Any(m => m.ValueText == "partial");
- static bool GetStaticClasses(ClassDeclarationSyntax x) => x.Modifiers.Any(m => m.ValueText == "static");
- static readonly string prototypeAttributeName = typeof(FactoryPrototypeMarkerAttribute<,,>).GetTruncatedTypeName().Replace("Attribute", string.Empty);
- static readonly string realizationAttributeName = typeof(FactoryRealizationAttribute).GetTruncatedTypeName().Replace("Attribute", string.Empty);
- static readonly string handleAttributeName = typeof(FactoryHandlerAttribute<>).GetTruncatedTypeName().Replace("Attribute", string.Empty);
- INamedTypeSymbol GetSymbolTypeFromAttribute(AttributeSyntax attribute, SemanticModel semanticModel)
- {
- var attrTypeInfo = semanticModel.GetTypeInfo(attribute);
- return ((INamedTypeSymbol)attrTypeInfo.Type!);
- }
- bool isGeneric;
- ICollection<string>? classTypes;
- public void Execute(GeneratorExecutionContext context)
- {
- var compilation = context.Compilation;
- foreach (var syntaxTree in compilation.SyntaxTrees)
- {
- var semanticModel = compilation.GetSemanticModel(syntaxTree);
- var isGenericClass = syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
- .FirstOrDefault()?.TypeParameterList?.Parameters.Count > 0;
- if (isGenericClass)
- {
- classTypes = syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
- .FirstOrDefault()
- .TypeParameterList?.Parameters.Select(e => e.Identifier.Text)
- .ToList();
- }
- var targetTypes = syntaxTree.GetRoot().DescendantNodes()
- .OfType<ClassDeclarationSyntax>()
- .Where(x => x.ContainsClassAttribute(realizationAttributeName))
- .Select(e => new
- {
- Target = e,
- FactoryName = semanticModel.GetConstantValue(e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(realizationAttributeName)))
- .Select(f => f?.ArgumentList.Arguments.FirstOrDefault().Expression)
- .FirstOrDefault()).Value.ToString()
- })
- //.Select(x => semanticModel.GetDeclaredSymbol(x))
- //.OfType<ITypeSymbol>()
- .ToImmutableList();
- var factoryPrototypes = syntaxTree.GetRoot().DescendantNodes()
- .Where(x => x is ClassDeclarationSyntax)
- .Cast<ClassDeclarationSyntax>()
- .Where(GetStaticClasses)
- .Where(e => e.ContainsClassAttribute(prototypeAttributeName))
- .Select(e => new
- {
- Prototype = e,
- FactoryName = semanticModel.GetConstantValue(e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(prototypeAttributeName)))
- .Select(f => f?.ArgumentList.Arguments.FirstOrDefault().Expression)
- .FirstOrDefault()).Value.ToString(),
- GenericTypes = e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(prototypeAttributeName)))
- .Select(f => GetSymbolTypeFromAttribute(f, semanticModel))
- .FirstOrDefault().TypeArguments
- //.Select(e => e.OriginalDefinition.Name.ToString())
- .ToList(),
- })
- .ToImmutableList();
- var pairs = targetTypes.Join(factoryPrototypes,
- e => e.FactoryName,
- f => f.FactoryName,
- (target, prototype) => new Combination(target.Target, prototype.Prototype, prototype.GenericTypes, target.FactoryName))
- .ToList();
- foreach (var pair in pairs)
- {
- var genericAddon = isGeneric ? $@"<{string.Join(",", classTypes)}>" : "";
- var targetSymbol = semanticModel.GetDeclaredSymbol(pair.Target);
- string source = GenerateFactory(pair, semanticModel, genericAddon);
- context.AddSource($"{targetSymbol.Name}.Factory.{pair.FactoryName}.cs", SourceText.From(source, Encoding.UTF8));
- }
- }
- }
- string GenerateFactory(Combination combination, SemanticModel semanticModel, string genericAddon)
- {
- var targetSymbol = semanticModel.GetDeclaredSymbol(combination.Target);
- var prototypeSymbol = semanticModel.GetDeclaredSymbol(combination.Prototype);
- var generatedResult = $@"
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Runtime.CompilerServices;
- using System.Text;
- using System.Threading.Tasks;
- using {prototypeSymbol.ContainingNamespace};
- namespace {targetSymbol.ContainingNamespace}
- {{
- {PublishFactoryPrototype(combination, semanticModel)}
- public partial class {targetSymbol.Name}{genericAddon}
- {{
- public {combination.FactoryName} {combination.FactoryName} {{get; protected set;}} = {combination.FactoryName}.Instance;
- }}
- }}";
- return generatedResult;
- }
- string PublishFactoryPrototype(Combination combination, SemanticModel semanticModel)
- {
- var prototypeMethods = combination.Prototype.Members.Select(e => e as MethodDeclarationSyntax)
- .Where(e => e != null)
- .Where(e => e.AttributeLists.SelectMany(f => f.Attributes).Where(s => s.Name.ToString().Contains(handleAttributeName)).Any())
- .Select(e => new
- {
- Method = e,
- ParamValue = e.AttributeLists.SelectMany(f => f.Attributes.Where(s => s.Name.ToString().Contains(handleAttributeName)))
- .Select(f => f?.ArgumentList.Arguments.FirstOrDefault()
- .Expression
- //.NormalizeWhitespace()
- .ToString()).FirstOrDefault(),
- })
- .ToList();
- var TKey = combination.GenericTypes.ElementAt(0).OriginalDefinition.Name.ToString();
- var TRequest = combination.GenericTypes.ElementAt(1).OriginalDefinition.Name.ToString();
- var TResponse = combination.GenericTypes.ElementAt(2).OriginalDefinition.Name.ToString();
- var dictionaryInitializer = new StringBuilder();
- foreach(var prototypeMethod in prototypeMethods)
- {
- dictionaryInitializer.AppendLine($@"methods.Add({prototypeMethod.ParamValue}, {combination.Prototype.Identifier}.{prototypeMethod.Method.Identifier});");
- }
- var initializer = $@"private void Initialize()
- {{
- Console.WriteLine(""xyu"");
- {dictionaryInitializer}
- }}";
- var handler = $@"public async Task<{TResponse}> Handle({TKey} key, {TRequest} request)
- {{
- if (methods.TryGetValue(key, out var method))
- {{
- return await method(request);
- }}
- throw new NotImplementedException();
- }}";
- var classBuilder = $@"public sealed class {combination.FactoryName}
- {{
- Dictionary<{TKey}, Func<{TRequest},Task<{TResponse}>>> methods = new();
- static readonly Lazy<{combination.FactoryName}> lazy = new(() => new());
- public static {combination.FactoryName} Instance {{ get => lazy.Value; }}
- private {combination.FactoryName}()
- {{
- Initialize();
- }}
- {initializer}
- {handler}
- }}";
- return classBuilder.ToString();
- }
- public void Initialize(GeneratorInitializationContext context)
- {
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment