diff --git a/ConfigurationProcessor.DependencyInjection.sln b/ConfigurationProcessor.DependencyInjection.sln
index 12e2b9f..6bbcc18 100644
--- a/ConfigurationProcessor.DependencyInjection.sln
+++ b/ConfigurationProcessor.DependencyInjection.sln
@@ -41,7 +41,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfigurationProcessor.Depe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWebApiGenerator", "sample\TestWebApiGenerator\TestWebApiGenerator.csproj", "{0945131E-BEAB-4EE0-86FE-A2C44300CCA1}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfigurationProcessor.Gen.DependencyInjection", "src\ConfigurationProcessor.Gen.DependencyInjection\ConfigurationProcessor.Gen.DependencyInjection.csproj", "{E22B4D07-D154-480D-B27A-7B64E14669D4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfigurationProcessor.DependencyInjection.SourceGeneration", "src\ConfigurationProcessor.DependencyInjection.SourceGeneration\ConfigurationProcessor.DependencyInjection.SourceGeneration.csproj", "{06738AF5-221D-44C4-AD3D-3377CA4C1BEC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests", "tests\ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests\ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests.csproj", "{1FA1DC9D-FF01-4B92-9EB0-5551F4016553}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -93,10 +95,14 @@ Global
{0945131E-BEAB-4EE0-86FE-A2C44300CCA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0945131E-BEAB-4EE0-86FE-A2C44300CCA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0945131E-BEAB-4EE0-86FE-A2C44300CCA1}.Release|Any CPU.Build.0 = Release|Any CPU
- {E22B4D07-D154-480D-B27A-7B64E14669D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E22B4D07-D154-480D-B27A-7B64E14669D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E22B4D07-D154-480D-B27A-7B64E14669D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E22B4D07-D154-480D-B27A-7B64E14669D4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {06738AF5-221D-44C4-AD3D-3377CA4C1BEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06738AF5-221D-44C4-AD3D-3377CA4C1BEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06738AF5-221D-44C4-AD3D-3377CA4C1BEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {06738AF5-221D-44C4-AD3D-3377CA4C1BEC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1FA1DC9D-FF01-4B92-9EB0-5551F4016553}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1FA1DC9D-FF01-4B92-9EB0-5551F4016553}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1FA1DC9D-FF01-4B92-9EB0-5551F4016553}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1FA1DC9D-FF01-4B92-9EB0-5551F4016553}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -113,7 +119,8 @@ Global
{53B667D5-FB51-4C83-A040-31724EC96F30} = {645ABE25-B876-4372-8712-C66AA34020FC}
{AD8581E7-B99F-42D8-BBA9-39D631F1F496} = {60E2AA27-F390-4152-9039-A05388E2D3D8}
{0945131E-BEAB-4EE0-86FE-A2C44300CCA1} = {645ABE25-B876-4372-8712-C66AA34020FC}
- {E22B4D07-D154-480D-B27A-7B64E14669D4} = {60E2AA27-F390-4152-9039-A05388E2D3D8}
+ {06738AF5-221D-44C4-AD3D-3377CA4C1BEC} = {60E2AA27-F390-4152-9039-A05388E2D3D8}
+ {1FA1DC9D-FF01-4B92-9EB0-5551F4016553} = {14052F2C-4DA2-45FC-8F05-33B7AC728494}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2358CB49-45E6-4E83-85C1-F995C676A43F}
diff --git a/sample/TestWebApiGenerator/ServiceRegistrationExtensions.cs b/sample/TestWebApiGenerator/ServiceRegistrationExtensions.cs
index a7f52ea..e5f0151 100644
--- a/sample/TestWebApiGenerator/ServiceRegistrationExtensions.cs
+++ b/sample/TestWebApiGenerator/ServiceRegistrationExtensions.cs
@@ -8,94 +8,6 @@ internal static partial class ServiceRegistrationExtensions
[GenerateServiceRegistration("Services")]
public static partial void AddServicesFromConfiguration(this WebApplicationBuilder builder);
- //[GenerateServiceRegistration("Services")]
- //internal static partial void AddServicesFromConfiguration(this IServiceCollection services, IConfiguration configurationb);
-}
-
-///
-/// Target codegen output.
-///
-static partial class ServiceRegistrationExtensions
-{
- //public static partial void AddServicesFromConfiguration(this WebApplicationBuilder app)
- // => app.Services.AddServicesFromConfiguration(app.Configuration);
-
- public static void AddServicesFromConfigurationX(this IServiceCollection services, IConfiguration configuration)
- {
- var servicesSection = configuration.GetSection("Services");
- if (!servicesSection.Exists())
- {
- return;
- }
-
- if (servicesSection.GetValue("Logging"))
- {
- services.AddLogging();
- }
-
- var hstsSection = servicesSection.GetSection("Hsts");
- if (hstsSection.Exists())
- {
- services.AddHsts(x =>
- {
- if (hstsSection.GetValue("ExcludedHosts:Clear"))
- {
- x.ExcludedHosts.Clear();
- }
-
- x.Preload = hstsSection.GetValue("Preload");
- x.IncludeSubDomains = hstsSection.GetValue("IncludeSubDomains");
- x.MaxAge = TimeSpan.Parse(hstsSection.GetValue("MaxAge"));
- });
- }
-
- var configureSection = servicesSection.GetSection("Configure");
- if (configureSection.Exists())
- {
- services.Configure(options =>
- {
- options.HttpOnly = configureSection.GetValue("HttpOnly");
- options.Secure = configureSection.GetValue("Secure");
- });
- }
-
- if (servicesSection.GetValue("Controllers"))
- {
- services.AddControllers();
- }
-
- if (servicesSection.GetValue("EndpointsApiExplorer"))
- {
- services.AddEndpointsApiExplorer();
- }
-
- if (servicesSection.GetValue("SwaggerGen"))
- {
- services.AddSwaggerGen();
- }
-
- var openTelemetrySection = servicesSection.GetSection("OpenTelemetryTracing");
- services.AddOpenTelemetryTracing(options =>
- {
- if (openTelemetrySection.GetValue("AspNetCoreInstrumentation"))
- {
- options.AddAspNetCoreInstrumentation();
- }
-
- if (openTelemetrySection.GetValue("HttpClientInstrumentation"))
- {
- options.AddHttpClientInstrumentation();
- }
-
- if (openTelemetrySection.GetValue("SqlClientInstrumentation"))
- {
- options.AddSqlClientInstrumentation();
- }
-
- if (openTelemetrySection.GetValue("JaegerExporter"))
- {
- options.AddJaegerExporter();
- }
- });
- }
+ // [GenerateServiceRegistration("Services")]
+ // internal static partial void AddServicesFromConfiguration(this IServiceCollection services, IConfiguration configuration);
}
diff --git a/sample/TestWebApiGenerator/TestWebApiGenerator.csproj b/sample/TestWebApiGenerator/TestWebApiGenerator.csproj
index dc2a457..0628a3a 100644
--- a/sample/TestWebApiGenerator/TestWebApiGenerator.csproj
+++ b/sample/TestWebApiGenerator/TestWebApiGenerator.csproj
@@ -5,8 +5,8 @@
net7.0
enable
enable
- true
- $(BaseIntermediateOutputPath)\GeneratedFiles
+
@@ -20,7 +20,7 @@
-
+
diff --git a/src/ConfigurationProcessor.DependencyInjection.Generator/ConfigurationProcessor.DependencyInjection.Generator.csproj b/src/ConfigurationProcessor.DependencyInjection.Generator/ConfigurationProcessor.DependencyInjection.Generator.csproj
index d812a1e..310ff0c 100644
--- a/src/ConfigurationProcessor.DependencyInjection.Generator/ConfigurationProcessor.DependencyInjection.Generator.csproj
+++ b/src/ConfigurationProcessor.DependencyInjection.Generator/ConfigurationProcessor.DependencyInjection.Generator.csproj
@@ -5,21 +5,17 @@
11
true
enable
- 0.1.0-beta.5
+ 0.1.0-beta.6
$(PackageTags);source generation
Generator
-
+
-
-
-
-
@@ -27,13 +23,9 @@
-
-
-
-
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/ConfigurationProcessor.Gen.DependencyInjection.csproj b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/ConfigurationProcessor.DependencyInjection.SourceGeneration.csproj
similarity index 70%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/ConfigurationProcessor.Gen.DependencyInjection.csproj
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/ConfigurationProcessor.DependencyInjection.SourceGeneration.csproj
index 7ad475c..eb1bb27 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/ConfigurationProcessor.Gen.DependencyInjection.csproj
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/ConfigurationProcessor.DependencyInjection.SourceGeneration.csproj
@@ -27,14 +27,11 @@
+
-
-
-
-
@@ -47,12 +44,8 @@
-
-
-
-
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Core/CoreCompatExtensions.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Core/CoreCompatExtensions.cs
similarity index 100%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Core/CoreCompatExtensions.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Core/CoreCompatExtensions.cs
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/ResolutionContext.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Core/ResolutionContext.cs
similarity index 96%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/ResolutionContext.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Core/ResolutionContext.cs
index 0107dde..0b46937 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/ResolutionContext.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Core/ResolutionContext.cs
@@ -1,5 +1,5 @@
using System.Reflection;
-using ConfigurationProcessor.Gen.DependencyInjection.Utility;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Utility;
using Microsoft.Extensions.Configuration;
namespace ConfigurationProcessor.Core.Implementation;
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Emitter.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Emitter.cs
similarity index 53%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Emitter.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Emitter.cs
index af2fcd0..a0cccb7 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Emitter.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Emitter.cs
@@ -1,34 +1,24 @@
using System.Reflection;
-using ConfigurationProcessor.Gen.DependencyInjection.Parsing;
-using ConfigurationProcessor.Gen.DependencyInjection.Utility;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Utility;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Configuration;
-namespace ConfigurationProcessor.Gen.DependencyInjection;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration;
internal class Emitter
{
- private GeneratorExecutionContext context;
- private readonly Action reportDiagnostic;
-
- public Emitter(GeneratorExecutionContext context, Action reportDiagnostic)
- {
- this.context = context;
- this.reportDiagnostic = reportDiagnostic;
- }
-
- public string Emit(IReadOnlyList generateConfigurationClasses, CancellationToken cancellationToken)
+ public string Emit(IReadOnlyList generateConfigurationClasses, List references, CancellationToken cancellationToken)
{
- var paths = context.Compilation.ExternalReferences.Select(x => x.Display!).ToList();
- var resolver = new PathAssemblyResolver(paths);
- var mlc = new MetadataLoadContext(resolver);
-
- var references = context.Compilation.ExternalReferences.Select(x => mlc.LoadFromAssemblyPath(x.Display!)).ToList();
-
var emitContext = new EmitContext(generateConfigurationClasses.First().Namespace, references);
foreach (var configClass in generateConfigurationClasses)
{
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
emitContext.Write($@"
namespace {configClass.Namespace}
{{
@@ -40,14 +30,6 @@ static partial class {configClass.Name}
foreach (var configMethod in configClass.Methods)
{
var sectionName = configMethod.ConfigurationSectionName;
- var configFile = configMethod.FileName;
-
- var jsonFile = context.AdditionalFiles.FirstOrDefault(x => Path.GetFileName(x.Path) == configFile);
- if (jsonFile == null)
- {
- Diag(DiagnosticDescriptors.ConfigurationFileNotFound, configMethod.Location, configMethod.FileName);
- continue;
- }
string configSectionVariableName = "servicesSection";
@@ -62,7 +44,7 @@ static partial class {configClass.Name}
}}");
emitContext.IncreaseIndent();
- BuildMethods(emitContext, jsonFile.Path, sectionName, configMethod.ServiceCollectionField!, configSectionVariableName);
+ BuildMethods(emitContext, configMethod.ConfigurationValues, sectionName, configMethod.ServiceCollectionField!, configSectionVariableName);
emitContext.DecreaseIndent();
emitContext.Write("}");
}
@@ -78,10 +60,10 @@ static partial class {configClass.Name}
return emitContext.ToString();
}
- private void BuildMethods(EmitContext emitContext, string jsonFilePath, string sectionName, string targetExpression, string configSectionVariableName)
+ private void BuildMethods(EmitContext emitContext, IEnumerable> configurationValues, string sectionName, string targetExpression, string configSectionVariableName)
{
var configBuilder = new ConfigurationBuilder();
- configBuilder.AddJsonFile(jsonFilePath);
+ configBuilder.AddInMemoryCollection(configurationValues);
var config = configBuilder.Build();
var directive = config.GetSection(sectionName);
@@ -95,9 +77,4 @@ private void BuildMethods(EmitContext emitContext, string jsonFilePath, string s
Parser.ServiceCollectionTypeName,
serviceCollectionParameterName);
}
-
- private void Diag(DiagnosticDescriptor desc, Location? location, params object?[]? messageArgs)
- {
- reportDiagnostic(Diagnostic.Create(desc, location, messageArgs));
- }
}
\ No newline at end of file
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Parser.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parser.cs
similarity index 93%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Parser.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parser.cs
index 521ceca..9fabff2 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Parser.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parser.cs
@@ -1,46 +1,46 @@
using System.Collections.Immutable;
using System.Diagnostics;
-using ConfigurationProcessor.Gen.DependencyInjection.Parsing;
-using ConfigurationProcessor.Gen.DependencyInjection.Utility;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Utility;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-namespace ConfigurationProcessor.Gen.DependencyInjection;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration;
internal class Parser
{
internal const string DefaultConfigurationFile = "appsettings.json";
internal const string GenerateServiceRegistrationAttribute = "ConfigurationProcessor.DependencyInjection.GenerateServiceRegistrationAttribute";
internal const string ServiceCollectionTypeName = "Microsoft.Extensions.DependencyInjection.IServiceCollection";
- private readonly Compilation compilation;
+ private readonly GeneratorExecutionContext context;
private readonly Action reportDiagnostic;
private readonly CancellationToken cancellationToken;
- public Parser(Compilation compilation, Action reportDiagnostic, CancellationToken cancellationToken)
+ public Parser(GeneratorExecutionContext context, Action reportDiagnostic, CancellationToken cancellationToken)
{
- this.compilation = compilation;
+ this.context = context;
this.reportDiagnostic = reportDiagnostic;
this.cancellationToken = cancellationToken;
}
internal IReadOnlyList GetServiceRegistrationClasses(IEnumerable classes)
{
- INamedTypeSymbol? generateServiceRegistrationAttribute = compilation.GetBestTypeByMetadataName(GenerateServiceRegistrationAttribute);
+ INamedTypeSymbol? generateServiceRegistrationAttribute = context.Compilation.GetBestTypeByMetadataName(GenerateServiceRegistrationAttribute);
if (generateServiceRegistrationAttribute == null)
{
// nothing to do if this type isn't available
return Array.Empty();
}
- INamedTypeSymbol? serviceCollectionSymbol = compilation.GetBestTypeByMetadataName(ServiceCollectionTypeName);
+ INamedTypeSymbol? serviceCollectionSymbol = context.Compilation.GetBestTypeByMetadataName(ServiceCollectionTypeName);
if (serviceCollectionSymbol == null)
{
// nothing to do if this type isn't available
return Array.Empty();
}
- INamedTypeSymbol? configurationSymbol = compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Configuration.IConfiguration");
+ INamedTypeSymbol? configurationSymbol = context.Compilation.GetBestTypeByMetadataName("Microsoft.Extensions.Configuration.IConfiguration");
if (configurationSymbol == null)
{
// nothing to do if this type isn't available
@@ -55,7 +55,7 @@ internal IReadOnlyList GetServiceRegistrationClasses(I
foreach (IGrouping group in classes.GroupBy(x => x.SyntaxTree))
{
SyntaxTree syntaxTree = group.Key;
- SemanticModel sm = compilation.GetSemanticModel(syntaxTree);
+ SemanticModel sm = context.Compilation.GetSemanticModel(syntaxTree);
foreach (ClassDeclarationSyntax classDec in group)
{
@@ -170,7 +170,20 @@ internal IReadOnlyList GetServiceRegistrationClasses(I
break;
}
- var lm = new ServiceRegistrationMethod(configurationMethodSymbol.Name, string.Join(", ", configurationMethodSymbol.Parameters.Select(ToDisplay)), method.Modifiers.ToString(), configurationFile ?? DefaultConfigurationFile, configurationSection, method.GetLocation());
+ var configFile = configurationFile ?? DefaultConfigurationFile;
+ IDictionary configurationValues;
+ var jsonFile = context.AdditionalFiles.FirstOrDefault(x => Path.GetFileName(x.Path) == configFile);
+ if (jsonFile == null)
+ {
+ Diag(DiagnosticDescriptors.ConfigurationFileNotFound, method.GetLocation(), configFile);
+ continue;
+ }
+ else
+ {
+ configurationValues = JsonConfigurationFileParser.Parse(File.OpenRead(jsonFile.Path));
+ }
+
+ var lm = new ServiceRegistrationMethod(configurationMethodSymbol.Name, string.Join(", ", configurationMethodSymbol.Parameters.Select(ToDisplay)), method.Modifiers.ToString(), configurationValues, configurationSection);
static string ToDisplay(IParameterSymbol parameter)
{
@@ -481,7 +494,7 @@ static bool IsAllowedKind(SyntaxKind kind) =>
static object? GetItem(TypedConstant arg) => arg.Kind == TypedConstantKind.Array ? arg.Values : arg.Value;
}
- if (results.Count > 0 && compilation is CSharpCompilation { LanguageVersion: LanguageVersion version and < LanguageVersion.CSharp8 })
+ if (results.Count > 0 && context.Compilation is CSharpCompilation { LanguageVersion: LanguageVersion version and < LanguageVersion.CSharp8 })
{
// we only support C# 8.0 and above
Diag(DiagnosticDescriptors.GenerateConfigurationUnsupportedLanguageVersion, null, version.ToDisplayString(), LanguageVersion.CSharp8.ToDisplayString());
@@ -535,7 +548,7 @@ private void Diag(DiagnosticDescriptor desc, Location? location, params object?[
private bool IsBaseOrIdentity(ITypeSymbol source, ITypeSymbol dest)
{
- Conversion conversion = compilation.ClassifyConversion(source, dest);
+ Conversion conversion = context.Compilation.ClassifyConversion(source, dest);
return conversion.IsIdentity || (conversion.IsReference && conversion.IsImplicit);
}
}
\ No newline at end of file
diff --git a/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/JsonConfigurationFileParser.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/JsonConfigurationFileParser.cs
new file mode 100644
index 0000000..371af14
--- /dev/null
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/JsonConfigurationFileParser.cs
@@ -0,0 +1,123 @@
+using System.Diagnostics;
+using System.Text.Json;
+using Microsoft.Extensions.Configuration;
+
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
+
+///
+/// This implementation is copied from Microsoft.Extensions.Configuration.Json
+///
+internal sealed class JsonConfigurationFileParser
+{
+ private readonly Dictionary data = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ private readonly Stack paths = new Stack();
+
+ private JsonConfigurationFileParser()
+ {
+ }
+
+ public static IDictionary Parse(Stream input)
+ => new JsonConfigurationFileParser().ParseStream(input);
+
+ private IDictionary ParseStream(Stream input)
+ {
+ var jsonDocumentOptions = new JsonDocumentOptions
+ {
+ CommentHandling = JsonCommentHandling.Skip,
+ AllowTrailingCommas = true,
+ };
+
+ using (var reader = new StreamReader(input))
+ using (JsonDocument doc = JsonDocument.Parse(reader.ReadToEnd(), jsonDocumentOptions))
+ {
+ if (doc.RootElement.ValueKind != JsonValueKind.Object)
+ {
+ throw new FormatException(string.Format("Invalid top level json element {0}", doc.RootElement.ValueKind));
+ }
+
+ VisitObjectElement(doc.RootElement);
+ }
+
+ return data;
+ }
+
+ private void VisitObjectElement(JsonElement element)
+ {
+ var isEmpty = true;
+
+ foreach (JsonProperty property in element.EnumerateObject())
+ {
+ isEmpty = false;
+ EnterContext(property.Name);
+ VisitValue(property.Value);
+ ExitContext();
+ }
+
+ SetNullIfElementIsEmpty(isEmpty);
+ }
+
+ private void VisitArrayElement(JsonElement element)
+ {
+ int index = 0;
+
+ foreach (JsonElement arrayElement in element.EnumerateArray())
+ {
+ EnterContext(index.ToString());
+ VisitValue(arrayElement);
+ ExitContext();
+ index++;
+ }
+
+ SetNullIfElementIsEmpty(isEmpty: index == 0);
+ }
+
+ private void SetNullIfElementIsEmpty(bool isEmpty)
+ {
+ if (isEmpty && paths.Count > 0)
+ {
+ data[paths.Peek()] = null;
+ }
+ }
+
+ private void VisitValue(JsonElement value)
+ {
+#pragma warning disable SA1405 // Debug.Assert should provide message text
+ Debug.Assert(paths.Count > 0);
+#pragma warning restore SA1405 // Debug.Assert should provide message text
+
+ switch (value.ValueKind)
+ {
+ case JsonValueKind.Object:
+ VisitObjectElement(value);
+ break;
+
+ case JsonValueKind.Array:
+ VisitArrayElement(value);
+ break;
+
+ case JsonValueKind.Number:
+ case JsonValueKind.String:
+ case JsonValueKind.True:
+ case JsonValueKind.False:
+ case JsonValueKind.Null:
+ string key = paths.Peek();
+ if (data.ContainsKey(key))
+ {
+ throw new FormatException(string.Format("Key {0} is duplicated", key));
+ }
+
+ data[key] = value.ToString();
+ break;
+
+ default:
+ throw new FormatException(string.Format("Unsupported json token {0}", value.ValueKind));
+ }
+ }
+
+ private void EnterContext(string context) =>
+ paths.Push(paths.Count > 0 ?
+ paths.Peek() + ConfigurationPath.KeyDelimiter + context :
+ context);
+
+ private void ExitContext() => paths.Pop();
+}
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/ServiceRegistrationClass.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/ServiceRegistrationClass.cs
similarity index 81%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/ServiceRegistrationClass.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/ServiceRegistrationClass.cs
index f0a5101..ec1837c 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/ServiceRegistrationClass.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/ServiceRegistrationClass.cs
@@ -1,4 +1,4 @@
-namespace ConfigurationProcessor.Gen.DependencyInjection.Parsing;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
internal sealed class ServiceRegistrationClass
{
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/ServiceRegistrationMethod.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/ServiceRegistrationMethod.cs
similarity index 55%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/ServiceRegistrationMethod.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/ServiceRegistrationMethod.cs
index 1ff021e..743a045 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/ServiceRegistrationMethod.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/ServiceRegistrationMethod.cs
@@ -1,8 +1,8 @@
using Microsoft.CodeAnalysis;
-namespace ConfigurationProcessor.Gen.DependencyInjection.Parsing;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
-internal sealed record class ServiceRegistrationMethod(string Name, string Arguments, string Modifiers, string FileName, string ConfigurationSectionName, Location Location)
+internal sealed record class ServiceRegistrationMethod(string Name, string Arguments, string Modifiers, IEnumerable> ConfigurationValues, string ConfigurationSectionName)
{
public string UniqueName { get; set; } = string.Empty;
diff --git a/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/SymbolVisibility.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/SymbolVisibility.cs
new file mode 100644
index 0000000..34fc9f3
--- /dev/null
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Parsing/SymbolVisibility.cs
@@ -0,0 +1,8 @@
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
+
+internal enum SymbolVisibility
+{
+ Public,
+ Private,
+ Internal,
+}
\ No newline at end of file
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/ConfigurationRegistrationGenerator.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/RegistrationGenerator.cs
similarity index 80%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/ConfigurationRegistrationGenerator.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/RegistrationGenerator.cs
index 5d2bae1..dc65f5e 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/ConfigurationRegistrationGenerator.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/RegistrationGenerator.cs
@@ -2,19 +2,21 @@
// Copyright (c) almostchristian. All rights reserved.
// -------------------------------------------------------------------------------------------------
+using System.Reflection;
using System.Text;
-using ConfigurationProcessor.Gen.DependencyInjection.Parsing;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
-namespace ConfigurationProcessor.Gen.DependencyInjection;
+namespace ConfigurationProcessor;
///
/// Generates method for registration based on an appsetting.json file.
///
[Generator]
-public class ConfigurationRegistrationGenerator : ISourceGenerator
+public class RegistrationGenerator : ISourceGenerator
{
///
public void Initialize(GeneratorInitializationContext context)
@@ -35,12 +37,17 @@ public void Execute(GeneratorExecutionContext context)
System.Diagnostics.Debugger.Launch();
#endif
- var p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken);
+ var p = new Parser(context, context.ReportDiagnostic, context.CancellationToken);
IReadOnlyList registrationClasses = p.GetServiceRegistrationClasses(receiver.ClassDeclarations);
if (registrationClasses.Count > 0)
{
- var e = new Emitter(context, context.ReportDiagnostic);
- string result = e.Emit(registrationClasses, context.CancellationToken);
+ var paths = context.Compilation.ExternalReferences.Select(x => x.Display!).ToList();
+ var resolver = new PathAssemblyResolver(paths);
+ var mlc = new MetadataLoadContext(resolver);
+ var references = context.Compilation.ExternalReferences.Select(x => mlc.LoadFromAssemblyPath(x.Display!)).ToList();
+
+ var e = new Emitter();
+ string result = e.Emit(registrationClasses, references, context.CancellationToken);
context.AddSource("RegisterServices.g.cs", SourceText.From(result, Encoding.UTF8));
}
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/DiagnosticDescriptorHelper.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/DiagnosticDescriptorHelper.cs
similarity index 91%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Utility/DiagnosticDescriptorHelper.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/DiagnosticDescriptorHelper.cs
index bb8d527..ae4a041 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/DiagnosticDescriptorHelper.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/DiagnosticDescriptorHelper.cs
@@ -1,6 +1,6 @@
using Microsoft.CodeAnalysis;
-namespace ConfigurationProcessor.Gen.DependencyInjection.Utility;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Utility;
///
/// Helper methods for creating instances.
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/DiagnosticDescriptors.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/DiagnosticDescriptors.cs
similarity index 98%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Utility/DiagnosticDescriptors.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/DiagnosticDescriptors.cs
index 8f369d6..e17acd7 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/DiagnosticDescriptors.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/DiagnosticDescriptors.cs
@@ -1,6 +1,6 @@
using Microsoft.CodeAnalysis;
-namespace ConfigurationProcessor.Gen.DependencyInjection.Utility;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Utility;
internal static class DiagnosticDescriptors
{
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/EmitContext.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/EmitContext.cs
similarity index 98%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Utility/EmitContext.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/EmitContext.cs
index 9874a81..1cfc0ce 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/EmitContext.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/EmitContext.cs
@@ -2,7 +2,7 @@
using System.Reflection;
using System.Text;
-namespace ConfigurationProcessor.Gen.DependencyInjection.Utility;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Utility;
internal record class EmitContext(string Namespace, List References)
{
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/Helpers.cs b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/Helpers.cs
similarity index 98%
rename from src/ConfigurationProcessor.Gen.DependencyInjection/Utility/Helpers.cs
rename to src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/Helpers.cs
index e5f566c..3ac11d5 100644
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Utility/Helpers.cs
+++ b/src/ConfigurationProcessor.DependencyInjection.SourceGeneration/Utility/Helpers.cs
@@ -1,12 +1,10 @@
-using System.Linq;
-using System.Reflection;
+using System.Reflection;
using ConfigurationProcessor.Core.Implementation;
-using ConfigurationProcessor.Gen.DependencyInjection.Parsing;
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Configuration;
-using static System.Collections.Specialized.BitVector32;
-namespace ConfigurationProcessor.Gen.DependencyInjection.Utility;
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.Utility;
internal static class Helpers
{
diff --git a/src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/SymbolVisibility.cs b/src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/SymbolVisibility.cs
deleted file mode 100644
index 2ade24a..0000000
--- a/src/ConfigurationProcessor.Gen.DependencyInjection/Parsing/SymbolVisibility.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace ConfigurationProcessor.Gen.DependencyInjection.Parsing;
-
-internal enum SymbolVisibility
-{
- Public,
- Private,
- Internal,
-}
\ No newline at end of file
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 46bab05..2701936 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -90,8 +90,8 @@ v1.0.0
$(NoWarn);CA1062
-
- 10.0
+
+ 11
diff --git a/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests.csproj b/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests.csproj
new file mode 100644
index 0000000..b48bdb5
--- /dev/null
+++ b/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests.csproj
@@ -0,0 +1,39 @@
+
+
+
+ net6.0
+ enable
+ enable
+ 11
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
diff --git a/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/EmitterTests.cs b/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/EmitterTests.cs
new file mode 100644
index 0000000..c5aa3b2
--- /dev/null
+++ b/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/EmitterTests.cs
@@ -0,0 +1,101 @@
+using ConfigurationProcessor.DependencyInjection.SourceGeneration.Parsing;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.Extensions.DependencyModel;
+using System.Reflection;
+using System.Text;
+
+namespace ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests;
+
+public class EmitterTests
+{
+ [Fact]
+ public void AddServicesWithContextPaths()
+ {
+ TestConfig(
+ @"{
+ ""Services"": {
+ ""WithChildren"": {
+ ""SimpleString"": ""helloworld""
+ }
+ },
+ ""ComplexObject"": true
+}",
+ @"var sectionWithChildren = servicesSection.GetSection(""WithChildren"");
+if (sectionWithChildren.Exists())
+{
+ services.WithChildren(options =>
+ {
+ // options.SimpleString = sectionWithChildren.GetValue(""SimpleString"");
+ });
+}");
+ }
+
+ private static void TestConfig(string inputJson, string expectedCsharpFragment)
+ {
+ var emitter = new Emitter();
+
+ var configurationValues = JsonConfigurationFileParser.Parse(new MemoryStream(Encoding.UTF8.GetBytes(inputJson)));
+ var rc = new ServiceRegistrationClass
+ {
+ Name = "Test",
+ Namespace = "TestApp",
+ ParentClass = null,
+ Keyword = "class",
+ };
+ rc.Methods.Add(new ServiceRegistrationMethod("Register", "IServiceCollection services, IConfiguration configuration", "public partial void", configurationValues, "Services")
+ {
+ ConfigurationField = "configuration",
+ ServiceCollectionField = "services",
+ });
+
+ var assemblies = GetLoadedAssemblies();
+
+ var generatedCsharp = emitter.Emit(new[] { rc }, assemblies, default);
+ var expectedCsharp = @$"//
+
+namespace TestApp
+{{
+ static partial class Test
+ {{
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""ConfigurationProcessor.DependencyInjection.Generator"", ""0.1.0"")]
+ public partial void void Register(this IServiceCollection services, IConfiguration configuration)
+ {{
+ var servicesSection = configuration.GetSection(""Services"");
+ if (!servicesSection.Exists())
+ {{
+ return;
+ }}
+
+{IndentLines(expectedCsharpFragment, " ")}
+ }}
+ }}
+}}";
+
+ Assert.Equal(expectedCsharp, generatedCsharp.Trim());
+ }
+
+ private static List GetLoadedAssemblies()
+ {
+ var dependencyContext = DependencyContext.Default;
+
+ var query = from assemblyName in dependencyContext.RuntimeLibraries
+ .SelectMany(l => l.GetDefaultAssemblyNames(dependencyContext)).Distinct()
+ select assemblyName;
+
+ return query.Select(Assembly.Load).ToList();
+ }
+
+ private static string IndentLines(string input, string indent)
+ {
+ var lines = input.Split(Environment.NewLine);
+ var sb = new StringBuilder();
+ foreach (var line in lines)
+ {
+ sb.Append(indent);
+ sb.AppendLine(line);
+ }
+
+ return sb.ToString().TrimEnd();
+ }
+}
\ No newline at end of file
diff --git a/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/Usings.cs b/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/Usings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/tests/ConfigurationProcessor.DependencyInjection.SourceGeneration.UnitTests/Usings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file