diff --git a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs index c2f9b0d..7ce3184 100644 --- a/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs +++ b/LightweightIocContainer.FactoryGenerator/FactoryGenerator.cs @@ -19,6 +19,8 @@ public class FactoryGenerator : IIncrementalGenerator private const string GENERATED_FILE_HEADER = "//---GENERATED by FactoryGenerator! DO NOT EDIT!---"; private const string INDENT = " "; + private const string CLEAR_MULTITON_INSTANCE_METHOD_NAME = "ClearMultitonInstance"; + public void Initialize(IncrementalGeneratorInitializationContext context) { string? classNamespace = typeof(FactoryGenerator).Namespace; @@ -27,6 +29,7 @@ public class FactoryGenerator : IIncrementalGenerator IncrementalValuesProvider syntaxProvider = context.SyntaxProvider.CreateSyntaxProvider(IsCallToGenerateFactory, GetTypeArgument); context.RegisterSourceOutput(syntaxProvider.Collect(), GenerateTypeDependentClasses); + context.RegisterSourceOutput(syntaxProvider, GenerateFactory); } private string GenerateFactoryExtensionsClass(string? classNamespace, string className) @@ -148,10 +151,131 @@ public class FactoryGenerator : IIncrementalGenerator return stringBuilder.ToString(); } + + private string GenerateFactorySourceCode(ITypeSymbol typeSymbol) + { + string typeName = typeSymbol.Name; + string? typeNamespace = typeSymbol.ContainingNamespace.IsGlobalNamespace ? null : typeSymbol.ContainingNamespace.ToString(); + + StringBuilder stringBuilder = new(); + + stringBuilder.AppendLine(GENERATED_FILE_HEADER); + stringBuilder.AppendLine(); + + stringBuilder.AppendLine("using LightweightIocContainer;"); + stringBuilder.AppendLine(); + + if (typeNamespace is not null) + { + stringBuilder.AppendLine($"namespace {typeNamespace};"); + stringBuilder.AppendLine(); + } + + stringBuilder.AppendLine($"public class Generated{typeName}(IocContainer container) : {typeName}"); + stringBuilder.AppendLine("{"); + + foreach (ISymbol? member in typeSymbol.GetMembers()) + { + if (member is not IMethodSymbol method) + continue; + + stringBuilder.AppendLine(); + + if (!method.ReturnsVoid) //create method + { + stringBuilder.Append($"{INDENT}public {method.ReturnType.Name} {method.Name}"); + + if (method.IsGenericMethod) + stringBuilder.Append($"<{string.Join(", ", method.TypeParameters.Select(p => p.Name))}>"); + + stringBuilder.Append($"({string.Join(", ", method.Parameters.Select(GetParameterText))})"); + + if (method.IsGenericMethod) + { + foreach (ITypeParameterSymbol typeParameter in method.TypeParameters) + { + List parameterConstraints = GetParameterConstraints(typeParameter); + if (parameterConstraints.Count == 0) + continue; + + stringBuilder.Append($" where {typeParameter.Name} : {string.Join(", ", parameterConstraints)}"); + } + } + + stringBuilder.AppendLine(); + stringBuilder.AppendLine($"{INDENT}{{"); + + foreach (IParameterSymbol parameter in method.Parameters) + { + stringBuilder.AppendLine($"{INDENT}{INDENT}object? {parameter.Name}Value = {parameter.Name}"); + stringBuilder.AppendLine($"{INDENT}{INDENT}if ({parameter.Name}Value is null)"); + stringBuilder.AppendLine($"{INDENT}{INDENT}{INDENT}{parameter.Name}Value = new NullParameter(typeof({parameter.Type.Name}));"); + stringBuilder.AppendLine(); + } + + if (method.IsAsync) + stringBuilder.Append($"{INDENT}{INDENT}return await container.ResolveAsync<>("); //TODO: Get return type from Task<> + else + stringBuilder.Append($"{INDENT}{INDENT}return container.Resolve<{method.ReturnType.Name}>("); + + stringBuilder.Append(string.Join(", ", method.Parameters.Select(p => $"{p.Name}Value"))); + stringBuilder.AppendLine(");"); + + stringBuilder.AppendLine($"{INDENT}}}"); + } + else if (method.Name == CLEAR_MULTITON_INSTANCE_METHOD_NAME) + { + //TODO + } + } + + typeSymbol.GetMembers(); + + stringBuilder.AppendLine("}"); + + return stringBuilder.ToString(); + } private IEnumerable GetNamespacesOfTypes(ImmutableArray types) => types.OfType() .Select(s => s.ContainingNamespace.IsGlobalNamespace ? null : s.ContainingNamespace.ToString()) .OfType() .Distinct(); + + private string GetParameterText(IParameterSymbol parameter) + { + StringBuilder stringBuilder = new(); + stringBuilder.Append(parameter.Type.Name); + + if (parameter.NullableAnnotation == NullableAnnotation.Annotated) + stringBuilder.Append("?"); + + stringBuilder.Append($" {parameter.Name}"); + return stringBuilder.ToString(); + } + + private List GetParameterConstraints(ITypeParameterSymbol typeParameterSymbol) + { + List constraints = []; + + foreach (ITypeSymbol constraintType in typeParameterSymbol.ConstraintTypes) + constraints.Add(constraintType.Name); + + if (typeParameterSymbol.HasReferenceTypeConstraint) + constraints.Add("class"); + + if (typeParameterSymbol.HasValueTypeConstraint) + constraints.Add("struct"); + + if (typeParameterSymbol.HasConstructorConstraint) + constraints.Add("new()"); + + if (typeParameterSymbol.HasUnmanagedTypeConstraint) + constraints.Add("unmanaged"); + + if (typeParameterSymbol.HasNotNullConstraint) + constraints.Add("notnull"); + + return constraints; + } } \ No newline at end of file