Skip to content

Commit 9aaa85c

Browse files
committed
WIP FromKeyedServices
1 parent 9b4a4f8 commit 9aaa85c

File tree

6 files changed

+76
-9
lines changed

6 files changed

+76
-9
lines changed

sandbox/CliFrameworkBenchmark/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// This benchmark project is based on CliFx.Benchmarks.
1+
// This benchmark project is based on CliFx.Benchmarks.
22
// https://github.com/Tyrrrz/CliFx/tree/master/CliFx.Benchmarks/
33

44
using BenchmarkDotNet.Configs;

sandbox/GeneratorSandbox/Program.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#nullable enable
1+
#nullable enable
22

33
using ConsoleAppFramework;
44
using GeneratorSandbox;
@@ -38,14 +38,20 @@
3838

3939
var app = ConsoleApp.Create();
4040

41-
app.Add("run", (string project, ConsoleAppContext context) =>
41+
app.Add("run", ([FromKeyedServices("takoyaki")] List<int> testList, string project, ConsoleAppContext context) =>
4242
{
4343
// run --project foo.csproj -- --foo 100 --bar bazbaz
4444
Console.WriteLine(string.Join(" ", context.Arguments));
4545

4646
// --project foo.csproj
4747
Console.WriteLine(string.Join(" ", context.CommandArguments!));
4848

49+
//IServiceProvider ServiceProvider = null!;
50+
// ((Microsoft.Extensions.DependencyInjection.IKeyedServiceProvider)ServiceProvider).GetKeyedService(Type, "");
51+
52+
// FromKeyedServicesAttribute
53+
// IKeyedServiceProvider
54+
4955
// --foo 100 --bar bazbaz
5056
Console.WriteLine(string.Join(" ", context.EscapedArguments!));
5157
});

src/ConsoleAppFramework/Command.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Microsoft.CodeAnalysis;
1+
using Microsoft.CodeAnalysis;
22
using System.Text;
33

44
namespace ConsoleAppFramework;
@@ -159,9 +159,11 @@ public record class CommandParameter
159159
public object? DefaultValue { get; init; }
160160
public required EquatableTypeSymbol? CustomParserType { get; init; }
161161
public required bool IsFromServices { get; init; }
162+
public required bool IsFromKeyedServices { get; init; }
163+
public required object? KeyedServiceKey { get; init; }
162164
public required bool IsConsoleAppContext { get; init; }
163165
public required bool IsCancellationToken { get; init; }
164-
public bool IsParsable => !(IsFromServices || IsCancellationToken || IsConsoleAppContext);
166+
public bool IsParsable => !(IsFromServices || IsFromKeyedServices || IsCancellationToken || IsConsoleAppContext);
165167
public bool IsFlag => Type.SpecialType == SpecialType.System_Boolean;
166168
public required bool HasValidation { get; init; }
167169
public required int ArgumentIndex { get; init; } // -1 is not Argument, other than marked as [Argument]
@@ -330,6 +332,14 @@ public string ToTypeShortString()
330332
return IsNullableReference ? $"{t}?" : t;
331333
}
332334

335+
public string GetFormattedKeyedServiceKey()
336+
{
337+
if (KeyedServiceKey == null) return "null";
338+
339+
if (KeyedServiceKey is string) return $"\"{KeyedServiceKey}\"";
340+
return $"({KeyedServiceKey.GetType().FullName}){KeyedServiceKey.ToString()}";
341+
}
342+
333343
public override string ToString()
334344
{
335345
var sb = new StringBuilder();

src/ConsoleAppFramework/ConsoleAppGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Microsoft.CodeAnalysis;
1+
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis.CSharp.Syntax;
44
using System.Collections.Immutable;
@@ -31,7 +31,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3131
var name = x.Display;
3232
if (name == null) continue;
3333

34-
if (!hasDependencyInjection && name.EndsWith("Microsoft.Extensions.DependencyInjection.dll")) // BuildServiceProvider
34+
if (!hasDependencyInjection && name.EndsWith("Microsoft.Extensions.DependencyInjection.dll")) // BuildServiceProvider, IKeyedServiceProvider
3535
{
3636
hasDependencyInjection = true;
3737
continue;

src/ConsoleAppFramework/Emitter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Microsoft.CodeAnalysis;
1+
using Microsoft.CodeAnalysis;
22
using System.Reflection.Metadata;
33

44
namespace ConsoleAppFramework;
@@ -123,6 +123,12 @@ public void EmitRun(SourceBuilder sb, CommandWithId commandWithId, bool isRunAsy
123123
var type = parameter.Type.ToFullyQualifiedFormatDisplayString();
124124
sb.AppendLine($"var arg{i} = ({type})ServiceProvider!.GetService(typeof({type}))!;");
125125
}
126+
else if (parameter.IsFromKeyedServices)
127+
{
128+
var type = parameter.Type.ToFullyQualifiedFormatDisplayString();
129+
var line = $"var arg{i} = ({type})((Microsoft.Extensions.DependencyInjection.IKeyedServiceProvider)ServiceProvider).GetKeyedService(typeof({type}), {parameter.GetFormattedKeyedServiceKey()})!;";
130+
sb.AppendLine(line);
131+
}
126132
}
127133
sb.AppendLineIfExists(command.Parameters.AsSpan());
128134

src/ConsoleAppFramework/Parser.cs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Microsoft.CodeAnalysis;
1+
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp;
33
using Microsoft.CodeAnalysis.CSharp.Syntax;
44

@@ -321,6 +321,40 @@ internal class Parser(ConsoleAppFrameworkGeneratorOptions generatorOptions, Diag
321321
return identifier is "FromServices" or "FromServicesAttribute";
322322
});
323323

324+
object? keyedServiceKey = null;
325+
var isFromKeyedServices = x.AttributeLists.SelectMany(x => x.Attributes)
326+
.Any(x =>
327+
{
328+
var name = x.Name;
329+
if (x.Name is QualifiedNameSyntax qns)
330+
{
331+
name = qns.Right;
332+
}
333+
334+
var identifier = name.ToString();
335+
var result = identifier is "FromKeyedServices" or "FromKeyedServicesAttribute";
336+
if (result)
337+
{
338+
SemanticModel semanticModel = model; // we can use SemanticModel
339+
if (x.ArgumentList?.Arguments.Count > 0)
340+
{
341+
var argumentExpression = x.ArgumentList.Arguments[0].Expression;
342+
343+
var constantValue = semanticModel.GetConstantValue(argumentExpression);
344+
if (constantValue.HasValue)
345+
{
346+
keyedServiceKey = constantValue.Value;
347+
}
348+
else if (argumentExpression is TypeOfExpressionSyntax typeOf)
349+
{
350+
var typeInfo = semanticModel.GetTypeInfo(typeOf.Type);
351+
keyedServiceKey = typeInfo.Type;
352+
}
353+
}
354+
}
355+
return result;
356+
});
357+
324358
var hasArgument = x.AttributeLists.SelectMany(x => x.Attributes)
325359
.Any(x =>
326360
{
@@ -368,6 +402,8 @@ internal class Parser(ConsoleAppFrameworkGeneratorOptions generatorOptions, Diag
368402
HasValidation = hasValidation,
369403
IsCancellationToken = isCancellationToken,
370404
IsFromServices = isFromServices,
405+
IsFromKeyedServices = isFromKeyedServices,
406+
KeyedServiceKey = keyedServiceKey,
371407
Aliases = [],
372408
Description = "",
373409
ArgumentIndex = argumentIndex,
@@ -512,11 +548,18 @@ internal class Parser(ConsoleAppFrameworkGeneratorOptions generatorOptions, Diag
512548
{
513549
var customParserType = x.GetAttributes().FirstOrDefault(x => x.AttributeClass?.AllInterfaces.Any(y => y.Name == "IArgumentParser") ?? false);
514550
var hasFromServices = x.GetAttributes().Any(x => x.AttributeClass?.Name == "FromServicesAttribute");
551+
var hasFromKeyedServices = x.GetAttributes().Any(x => x.AttributeClass?.Name == "FromKeyedServicesAttribute");
515552
var hasArgument = x.GetAttributes().Any(x => x.AttributeClass?.Name == "ArgumentAttribute");
516553
var hasValidation = x.GetAttributes().Any(x => x.AttributeClass?.GetBaseTypes().Any(y => y.Name == "ValidationAttribute") ?? false);
517554
var isCancellationToken = SymbolEqualityComparer.Default.Equals(x.Type, wellKnownTypes.CancellationToken);
518555
var isConsoleAppContext = x.Type!.Name == "ConsoleAppContext";
519556

557+
object? keyedServiceKey = null;
558+
if (hasFromKeyedServices)
559+
{
560+
// TODO: try to get keyedservicekey
561+
}
562+
520563
string description = "";
521564
string[] aliases = [];
522565
if (parameterDescriptions != null && parameterDescriptions.TryGetValue(x.Name, out var desc))
@@ -554,6 +597,8 @@ internal class Parser(ConsoleAppFrameworkGeneratorOptions generatorOptions, Diag
554597
CustomParserType = customParserType?.AttributeClass?.ToEquatable(),
555598
IsCancellationToken = isCancellationToken,
556599
IsFromServices = hasFromServices,
600+
IsFromKeyedServices = hasFromKeyedServices,
601+
KeyedServiceKey = keyedServiceKey,
557602
HasValidation = hasValidation,
558603
Aliases = aliases,
559604
ArgumentIndex = argumentIndex,

0 commit comments

Comments
 (0)