Skip to content

Commit 6b2c1b2

Browse files
Name templates
1 parent 18c1211 commit 6b2c1b2

File tree

11 files changed

+1017
-367
lines changed

11 files changed

+1017
-367
lines changed

src/Pure.DI.Core/Components/Api.g.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,7 +1879,7 @@ internal interface IConfiguration
18791879
/// </example>
18801880
/// </summary>
18811881
/// <typeparam name="T">The type of dependency to be bound.</typeparam>
1882-
/// <param name="name">Specifies the unique name of the root of the composition. If the value is empty, a private root will be created, which can be used when calling <c>Resolve</c> methods.</param>
1882+
/// <param name="name">Specifies the name template of the root of the composition. If the value is empty, a private root will be created, which can be used when calling <c>Resolve</c> methods.</param>
18831883
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
18841884
/// <param name="tags">The optional argument that specifies tags for a particular type of dependency binding. If is is not empty, the first tag is used for the root.</param>
18851885
/// <returns>Reference to the setup continuation chain.</returns>
@@ -2016,7 +2016,7 @@ internal interface IConfiguration
20162016
/// </code>
20172017
/// </example>
20182018
/// </summary>
2019-
/// <param name="name">The argument name.</param>
2019+
/// <param name="name">The argument name template.</param>
20202020
/// <param name="tags">The optional argument that specifies the tags for the argument.</param>
20212021
/// <typeparam name="T">The argument type.</typeparam>
20222022
/// <returns>Reference to the setup continuation chain.</returns>
@@ -2031,7 +2031,7 @@ internal interface IConfiguration
20312031
/// </code>
20322032
/// </example>
20332033
/// </summary>
2034-
/// <param name="name">The argument name.</param>
2034+
/// <param name="name">The argument name tamplate.</param>
20352035
/// <param name="tags">The optional argument that specifies the tags for the argument.</param>
20362036
/// <typeparam name="T">The argument type.</typeparam>
20372037
/// <returns>Reference to the setup continuation chain.</returns>
@@ -2046,7 +2046,7 @@ internal interface IConfiguration
20462046
/// </code>
20472047
/// </example>
20482048
/// </summary>
2049-
/// <param name="name">Specifies the unique name of the root of the composition. If the value is empty, a private root will be created, which can be used when calling <c>Resolve</c> methods.</param>
2049+
/// <param name="name">Specifies the name template of the root of the composition. If the value is empty, a private root will be created, which can be used when calling <c>Resolve</c> methods.</param>
20502050
/// <param name="tag">Optional argument specifying the tag for the root of the Composition.</param>
20512051
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
20522052
/// <typeparam name="T">The Composition root type.</typeparam>
@@ -2062,10 +2062,11 @@ internal interface IConfiguration
20622062
/// </code>
20632063
/// </example>
20642064
/// </summary>
2065+
/// <param name="name">Specifies the name template of the roots of the composition. If the value is empty, private roots will be created, which can be used when calling <c>Resolve</c> methods.</param>
20652066
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
20662067
/// <typeparam name="T">The Composition root base type.</typeparam>
20672068
/// <returns>Reference to the setup continuation chain.</returns>
2068-
IConfiguration Roots<T>(RootKinds kind = RootKinds.Default);
2069+
IConfiguration Roots<T>(string name = "", RootKinds kind = RootKinds.Default);
20692070

20702071
/// <summary>
20712072
/// Specifies the method of the composition builder. The first argument to the method will always be the instance to be built. The remaining arguments to this method will be listed in the order in which they are defined in the setup.Specifies to create a composition builder method. The first argument to the method will always be the instance to be built. The remaining arguments to this method will be listed in the order in which they are defined in the setup.
@@ -2076,7 +2077,7 @@ internal interface IConfiguration
20762077
/// </code>
20772078
/// </example>
20782079
/// </summary>
2079-
/// <param name="name">Specifies the unique name of the builder. The default name is "BuildUp".</param>
2080+
/// <param name="name">Specifies the name template of the builder. The default name tamplate is "BuildUp".</param>
20802081
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
20812082
/// <typeparam name="T">The Composition root type.</typeparam>
20822083
/// <returns>Reference to the setup continuation chain.</returns>
@@ -2091,7 +2092,7 @@ internal interface IConfiguration
20912092
/// </code>
20922093
/// </example>
20932094
/// </summary>
2094-
/// <param name="name">Specifies the unique name of the builders. The default name is "BuildUp".</param>
2095+
/// <param name="name">Specifies the name template of the builders. The default name tamplate is "BuildUp".</param>
20952096
/// <param name="kind">The Optional argument specifying the kind for the root of the Composition.</param>
20962097
/// <typeparam name="T">The Composition root base type.</typeparam>
20972098
/// <returns>Reference to the setup continuation chain.</returns>
@@ -2962,27 +2963,26 @@ public IConfiguration RootArg<T>(string name, params object[] tags)
29622963
return Configuration.Shared;
29632964
}
29642965

2965-
29662966
/// <inheritdoc />
29672967
public IConfiguration Root<T>(string name, object tag, RootKinds rootKind)
29682968
{
29692969
return Configuration.Shared;
29702970
}
29712971

29722972
/// <inheritdoc />
2973-
public IConfiguration Roots<T>(RootKinds kind = RootKinds.Default)
2973+
public IConfiguration Roots<T>(string name, RootKinds kind)
29742974
{
29752975
return Configuration.Shared;
29762976
}
29772977

29782978
/// <inheritdoc />
2979-
public IConfiguration Builder<T>(string name = "BuildUp", RootKinds kind = RootKinds.Default)
2979+
public IConfiguration Builder<T>(string name, RootKinds kind)
29802980
{
29812981
return Configuration.Shared;
29822982
}
29832983

29842984
/// <inheritdoc />
2985-
public IConfiguration Builders<T>(string name = "BuildUp", RootKinds kind = RootKinds.Default)
2985+
public IConfiguration Builders<T>(string name, RootKinds kind = RootKinds.Default)
29862986
{
29872987
return Configuration.Shared;
29882988
}

src/Pure.DI.Core/Core/ApiInvocationProcessor.cs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ internal class ApiInvocationProcessor(
1111
ISemantic semantic,
1212
ISymbolNames symbolNames,
1313
[Tag(Tag.UniqueTag)] IdGenerator idGenerator,
14-
IBaseSymbolsProvider baseSymbolsProvider)
14+
IBaseSymbolsProvider baseSymbolsProvider,
15+
INameFormatter nameFormatter)
1516
: IApiInvocationProcessor
1617
{
1718
private static readonly char[] TypeNamePartsSeparators = ['.'];
@@ -285,8 +286,9 @@ MemberAccessExpressionSyntax memberAccess when memberAccess.Kind() == SyntaxKind
285286
return;
286287
}
287288

288-
var rootsArgs = arguments.GetArgs(invocation.ArgumentList, "kind");
289-
var kind = rootsArgs[0] is { } kindArg ? semantic.GetConstantValue<RootKinds>(semanticModel, kindArg.Expression) : RootKinds.Default;
289+
var rootsArgs = arguments.GetArgs(invocation.ArgumentList, "name", "kind");
290+
var rootsName = rootsArgs[0] is { } nameArg ? semantic.GetConstantValue<string>(semanticModel, nameArg.Expression) ?? "" : "";
291+
var kind = rootsArgs[1] is { } kindArg ? semantic.GetConstantValue<RootKinds>(semanticModel, kindArg.Expression) : RootKinds.Default;
290292
var rootTypes = semanticModel
291293
.LookupNamespacesAndTypes(invocation.Span.Start)
292294
.OfType<INamedTypeSymbol>()
@@ -296,7 +298,8 @@ MemberAccessExpressionSyntax memberAccess when memberAccess.Kind() == SyntaxKind
296298

297299
foreach (var rootType in rootTypes)
298300
{
299-
metadataVisitor.VisitRoot(new MdRoot(invocation, semanticModel, rootType, "", new MdTag(0, null), kind, invocationComments, false));
301+
var rootName = GetName((SyntaxNode?)rootsArgs[1] ?? invocation, semanticModel, rootsName, rootType) ?? "";
302+
metadataVisitor.VisitRoot(new MdRoot(invocation, semanticModel, rootType, rootName, new MdTag(0, null), kind, invocationComments, false));
300303
}
301304

302305
break;
@@ -496,7 +499,9 @@ private void VisitBuilder(
496499

497500
// Root
498501
var rootArgs = arguments.GetArgs(invocationSource.ArgumentList, "name", "tag", "kind");
499-
var builderName = rootArgs[0] is { } nameArg ? semantic.GetConstantValue<string>(semanticModel, nameArg.Expression) ?? Names.DefaultBuilderName : Names.DefaultBuilderName;
502+
var builderName = rootArgs[0] is { } nameArg ?
503+
GetName(nameArg, semanticModel, semantic.GetConstantValue<string>(semanticModel, nameArg.Expression), builderType) ?? Names.DefaultBuilderName
504+
: Names.DefaultBuilderName;
500505
var kind = rootArgs[2] is { } kindArg ? semantic.GetConstantValue<RootKinds>(semanticModel, kindArg.Expression) : RootKinds.Default;
501506
metadataVisitor.VisitRoot(new MdRoot(invocationSource, semanticModel, builderType, builderName, builderTag, kind, invocationComments, true));
502507
}
@@ -589,11 +594,13 @@ private void VisitRoot(
589594
SemanticModel semanticModel,
590595
InvocationExpressionSyntax invocation,
591596
IReadOnlyCollection<string> invocationComments,
592-
ITypeSymbol rootSymbol)
597+
INamedTypeSymbol rootSymbol)
593598
{
594599
var rootArgs = arguments.GetArgs(invocation.ArgumentList, "name", "tag", "kind");
595-
var name = rootArgs[0] is { } nameArg ? semantic.GetConstantValue<string>(semanticModel, nameArg.Expression) ?? "" : "";
596600
var tag = rootArgs[1] is { } tagArg ? semantic.GetConstantValue<object>(semanticModel, tagArg.Expression) : null;
601+
var name = rootArgs[0] is { } nameArg
602+
? GetName(nameArg, semanticModel, semantic.GetConstantValue<string>(semanticModel, nameArg.Expression), rootSymbol, tag) ?? ""
603+
: "";
597604
var kind = rootArgs[2] is { } kindArg ? semantic.GetConstantValue<RootKinds>(semanticModel, kindArg.Expression) : RootKinds.Default;
598605
metadataVisitor.VisitRoot(new MdRoot(invocation, semanticModel, rootSymbol, name, new MdTag(0, tag), kind, invocationComments, false));
599606
}
@@ -604,11 +611,13 @@ private void VisitRoot(
604611
SemanticModel semanticModel,
605612
InvocationExpressionSyntax invocation,
606613
IReadOnlyCollection<string> invocationComments,
607-
ITypeSymbol rootSymbol)
614+
INamedTypeSymbol rootSymbol)
608615
{
609616
tag ??= new MdTag(0, null);
610617
var rootArgs = arguments.GetArgs(invocation.ArgumentList, "name", "kind");
611-
var name = rootArgs[0] is { } nameArg ? semantic.GetConstantValue<string>(semanticModel, nameArg.Expression) ?? "" : "";
618+
var name = rootArgs[0] is { } nameArg
619+
? GetName(nameArg, semanticModel, semantic.GetConstantValue<string>(semanticModel, nameArg.Expression), rootSymbol, tag.Value.Value) ?? ""
620+
: "";
612621
var kind = rootArgs[1] is { } kindArg ? semantic.GetConstantValue<RootKinds>(semanticModel, kindArg.Expression) : RootKinds.Default;
613622
metadataVisitor.VisitRoot(new MdRoot(invocation, semanticModel, rootSymbol, name, tag, kind, invocationComments, false));
614623
}
@@ -903,4 +912,30 @@ private static CompositionName CreateCompositionName(
903912

904913
return new CompositionName(className, newNamespace, source);
905914
}
915+
916+
private string? GetName(
917+
SyntaxNode source,
918+
SemanticModel semanticModel,
919+
string? nameTemplate,
920+
INamedTypeSymbol? type = null,
921+
object? tag = null)
922+
{
923+
if (string.IsNullOrWhiteSpace(nameTemplate))
924+
{
925+
return nameTemplate;
926+
}
927+
928+
var name = nameFormatter.Format(semanticModel, nameTemplate, type, tag);
929+
if (string.IsNullOrWhiteSpace(name))
930+
{
931+
return name;
932+
}
933+
934+
if (!SyntaxFacts.IsValidIdentifier(name))
935+
{
936+
throw new CompileErrorException($"Invalid identifier \"{name}\".", source.GetLocation(), LogId.ErrorInvalidMetadata);
937+
}
938+
939+
return name;
940+
}
906941
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Pure.DI.Core;
2+
3+
internal interface INameFormatter
4+
{
5+
string? Format(
6+
SemanticModel semanticModel,
7+
string? nameTemplate,
8+
INamedTypeSymbol? type,
9+
object? tag);
10+
}

src/Pure.DI.Core/Core/InjectionSiteFactory.cs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,10 @@ internal class InjectionSiteFactory : IInjectionSiteFactory
1111
static InjectionSiteFactory()
1212
{
1313
NameTagQualifiedFormat = new SymbolDisplayFormat(
14-
genericsOptions:
15-
SymbolDisplayGenericsOptions.IncludeTypeParameters,
16-
typeQualificationStyle:
17-
SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
18-
memberOptions:
19-
SymbolDisplayMemberOptions.IncludeType |
20-
SymbolDisplayMemberOptions.IncludeContainingType,
21-
miscellaneousOptions:
22-
SymbolDisplayMiscellaneousOptions.UseSpecialTypes
23-
| SymbolDisplayMiscellaneousOptions.CollapseTupleTypes
14+
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
15+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
16+
memberOptions: SymbolDisplayMemberOptions.IncludeType | SymbolDisplayMemberOptions.IncludeContainingType,
17+
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.CollapseTupleTypes
2418
);
2519

2620
var qualifiedNameArityFormat = NameTagQualifiedFormat.GetType().GetField("QualifiedNameArityFormat", BindingFlags.Static | BindingFlags.NonPublic);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace Pure.DI.Core;
2+
3+
internal class NameFormatter : INameFormatter
4+
{
5+
private static readonly SymbolDisplayFormat TypeFormat = new(
6+
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
7+
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
8+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly,
9+
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.CollapseTupleTypes | SymbolDisplayMiscellaneousOptions.RemoveAttributeSuffix
10+
);
11+
12+
public string? Format(
13+
SemanticModel semanticModel,
14+
string? nameTemplate,
15+
INamedTypeSymbol? type,
16+
object? tag)
17+
{
18+
if (string.IsNullOrWhiteSpace(nameTemplate))
19+
{
20+
return nameTemplate;
21+
}
22+
23+
if (!nameTemplate.Contains('{'))
24+
{
25+
return nameTemplate;
26+
}
27+
28+
var name = nameTemplate!.Trim();
29+
if (type is not null)
30+
{
31+
name = name.Replace("{type}", type.ToDisplayString(NullableFlowState.NotNull, TypeFormat));
32+
}
33+
34+
if (tag is not null)
35+
{
36+
name = name.Replace("{tag}", tag.ToString());
37+
}
38+
39+
return name;
40+
}
41+
}

src/Pure.DI.Core/Generator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ private void Setup() => DI.Setup()
8383
.Bind().To<TriviaTools>()
8484
.Bind().To<InstanceDpProvider>()
8585
.Bind().To<Injections>()
86+
.Bind().To<NameFormatter>()
8687

8788
// Validators
8889
.Bind(Type).To<MetadataValidator>()

0 commit comments

Comments
 (0)