Skip to content

Commit 351d856

Browse files
author
Nikolay Pianikov
committed
Refactor ClassDiagramBuilder to improve type handling and formatting logic
1 parent 6774fee commit 351d856

2 files changed

Lines changed: 74 additions & 48 deletions

File tree

build/Core/Targets/ReadmeTarget.cs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,23 @@ private async Task GenerateExamplesAsync(IReadOnlyCollection<ExampleGroup> examp
249249
private static async Task AddClassDiagram(string logsDirectory, string exampleName, TextWriter writer)
250250
{
251251
var classDiagramFile = Path.Combine(logsDirectory, exampleName + ".Mermaid");
252-
if (File.Exists(classDiagramFile))
252+
if (!File.Exists(classDiagramFile))
253253
{
254-
await writer.WriteLineAsync("Class diagram:");
255-
await writer.WriteLineAsync();
256-
await writer.WriteLineAsync("```mermaid");
257-
var classDiagram = await File.ReadAllTextAsync(classDiagramFile);
258-
await writer.WriteLineAsync(classDiagram);
259-
await writer.WriteLineAsync("```");
254+
return;
260255
}
256+
257+
var classDiagram = await File.ReadAllTextAsync(classDiagramFile);
258+
var trimmed = classDiagram.TrimStart();
259+
if (trimmed.Length == 0 || (!trimmed.StartsWith("---") && !trimmed.StartsWith("classDiagram")))
260+
{
261+
return;
262+
}
263+
264+
await writer.WriteLineAsync("Class diagram:");
265+
await writer.WriteLineAsync();
266+
await writer.WriteLineAsync("```mermaid");
267+
await writer.WriteLineAsync(classDiagram);
268+
await writer.WriteLineAsync("```");
261269
}
262270

263271
private static async Task AddExample(string logsDirectory, string exampleSearchPattern, TextWriter writer)

src/Pure.DI.Core/Core/Code/ClassDiagramBuilder.cs

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ public Lines Build(CompositionCode composition)
3737
var lines = new Lines();
3838
lines.AppendLine("---");
3939
lines.AppendLine(" config:");
40-
lines.AppendLine($" maxTextSize: {int.MaxValue}");
41-
lines.AppendLine($" maxEdges: {int.MaxValue}");
4240
lines.AppendLine(" class:");
4341
lines.AppendLine(" hideEmptyMembersBox: true");
4442
lines.AppendLine("---");
@@ -83,13 +81,13 @@ public Lines Build(CompositionCode composition)
8381

8482
if (composition.TotalDisposablesCount > 0)
8583
{
86-
classes.Add(new Class(nameof(System), "IDisposable", "abstract", null, new Lines()));
84+
classes.Add(new Class(nameof(System), "IDisposable", "interface", null, new Lines()));
8785
lines.AppendLine($"{composition.Name.ClassName} --|> IDisposable");
8886
}
8987

9088
if (composition.AsyncDisposableCount > 0)
9189
{
92-
classes.Add(new Class(nameof(System), "IAsyncDisposable", "abstract", null, new Lines()));
90+
classes.Add(new Class(nameof(System), "IAsyncDisposable", "interface", null, new Lines()));
9391
lines.AppendLine($"{composition.Name.ClassName} --|> IAsyncDisposable");
9492
}
9593

@@ -103,16 +101,31 @@ public Lines Build(CompositionCode composition)
103101
}
104102

105103
var contracts = injectionsBuilder.Build(new ContractsBuildContext(node.Binding, MdTag.ContextTag, null));
106-
foreach (var contract in contracts)
104+
foreach (var contractGroup in contracts
105+
.Where(c => !types.TypeEquals(node.Type, c.Type))
106+
.GroupBy(c => c.Type, typeSymbolComparer.Dependency))
107107
{
108-
if (types.TypeEquals(node.Type, contract.Type))
108+
var contractType = contractGroup.First().Type;
109+
typeSymbols.Add(contractType);
110+
111+
var hasExplicitTag = contractGroup.Any(c => c.Tag is not null and not MdTagOnSites);
112+
string label;
113+
if (!hasExplicitTag)
109114
{
110-
continue;
115+
label = "";
116+
}
117+
else
118+
{
119+
var parts = contractGroup
120+
.Select(c => c.Tag is null or MdTagOnSites ? "default" : EscapeTag(c.Tag))
121+
.Distinct()
122+
.OrderBy(s => s == "default" ? 1 : 0)
123+
.ThenBy(s => s)
124+
.ToList();
125+
label = $" : {string.Join(", ", parts)}";
111126
}
112127

113-
typeSymbols.Add(contract.Type);
114-
var tag = FormatTag(contract.Tag);
115-
lines.AppendLine($"{FormatType(setup, node.Type, DefaultFormatOptions)} --|> {FormatType(setup, contract.Type, DefaultFormatOptions)}{(string.IsNullOrWhiteSpace(tag) ? "" : $" : {tag}")}");
128+
lines.AppendLine($"{FormatType(setup, node.Type, DefaultFormatOptions)} --|> {FormatType(setup, contractType, DefaultFormatOptions)}{label}");
116129
}
117130

118131
var classDiagramWalker = new ClassDiagramWalker(setup, marker, this, classes, DefaultFormatOptions, locationProvider);
@@ -158,7 +171,11 @@ public Lines Build(CompositionCode composition)
158171
}
159172

160173
var tags = arg.Binding.Contracts.SelectMany(i => i.Tags.Select(tag => tag.Value)).ToList();
161-
lines.AppendLine($"{targetType} o-- {sourceType} : {(tags.Count > 0 ? FormatTags(tags) + " " : "")}Argument \\\"{arg.Source.ArgName}\\\"");
174+
var formattedTags = tags.Count > 0 ? FormatTags(tags) : "";
175+
var argLabel = formattedTags.Length > 0
176+
? $"{formattedTags} Argument \\\"{arg.Source.ArgName}\\\""
177+
: $"Argument \\\"{arg.Source.ArgName}\\\"";
178+
lines.AppendLine($"{targetType} o-- {sourceType} : {argLabel}");
162179
}
163180
else
164181
{
@@ -168,7 +185,11 @@ public Lines Build(CompositionCode composition)
168185
}
169186

170187
var relationship = dependency.Source.Lifetime == Lifetime.Transient ? "*--" : "o--";
171-
lines.AppendLine($"{targetType} {relationship} {FormatCardinality(count, dependency.Source.Lifetime)} {sourceType} : {FormatDependency(setup, dependency, DefaultFormatOptions)}");
188+
var cardinality = FormatCardinality(count, dependency.Source.Lifetime);
189+
var head = cardinality.Length > 0
190+
? $"{targetType} {relationship} {cardinality} {sourceType}"
191+
: $"{targetType} {relationship} {sourceType}";
192+
lines.AppendLine($"{head} : {FormatDependency(setup, dependency, DefaultFormatOptions)}");
172193
}
173194
}
174195
}
@@ -232,53 +253,50 @@ private string FormatRoot(MdSetup setup, Root root)
232253
rootArgsStr = $"({string.Join(", ", root.RootArgs.Select(arg => $"{ResolveTypeName(setup, arg.InstanceType)} {arg.Name}"))})";
233254
}
234255

235-
var displayName = root.IsPublic ? root.DisplayName : "_";
236-
return $"{FormatType(setup, root.Injection.Type, DefaultFormatOptions)} {displayName}{typeArgsStr}{rootArgsStr}";
256+
return $"{FormatType(setup, root.Injection.Type, DefaultFormatOptions)} {root.DisplayName}{typeArgsStr}{rootArgsStr}";
237257
}
238258

239259
[SuppressMessage("ReSharper", "InvertIf")]
240260
private static string FormatCardinality(int count, Lifetime lifetime)
241261
{
242-
var cardinality = new StringBuilder();
262+
var parts = new List<string>(2);
243263
if (count > 1)
244264
{
245-
cardinality.Append(count);
246-
cardinality.Append(' ');
265+
parts.Add(count.ToString());
247266
}
248267

249268
if (lifetime != Lifetime.Transient)
250269
{
251-
cardinality.Append(lifetime);
252-
if (count > 1)
253-
{
254-
cardinality.Append(" instances");
255-
}
270+
parts.Add(lifetime.ToString());
256271
}
257272

258-
if (cardinality.Length > 0)
273+
if (count > 1)
259274
{
260-
cardinality.Insert(0, "\\\"");
261-
cardinality.Append("\\\"");
275+
parts.Add("instances");
262276
}
263277

264-
return cardinality.ToString();
278+
return parts.Count == 0 ? "" : $"\\\"{string.Join(" ", parts)}\\\"";
265279
}
266280

267-
private string FormatDependency(MdSetup setup, Dependency dependency, FormatOptions options) =>
268-
$"{(dependency.Injection.Tag is null or MdTagOnSites ? "" : FormatTag(dependency.Injection.Tag) + " ")}{FormatSymbol(setup, dependency.Injection.Type, options)}";
281+
private string FormatDependency(MdSetup setup, Dependency dependency, FormatOptions options)
282+
{
283+
var tag = FormatTag(dependency.Injection.Tag);
284+
var symbol = FormatSymbol(setup, dependency.Injection.Type, options);
285+
return tag.Length == 0 ? symbol : $"{tag} {symbol}";
286+
}
269287

270288
private static string FormatTag(object? tag) =>
271289
tag is null or MdTagOnSites
272290
? ""
273-
: EscapeTag(tag) + " ";
291+
: EscapeTag(tag);
274292

275293
private static string EscapeTag(object tag) =>
276294
tag.ValueToString("")
277295
.Replace("\"", "\\\"")
278296
.Replace(':', '﹕');
279297

280298
private static string FormatTags(IEnumerable<object?> tags) =>
281-
string.Join(", ", tags.Distinct().Select(FormatTag).OrderBy(i => i));
299+
string.Join(", ", tags.Distinct().Select(FormatTag).Where(s => s.Length > 0).OrderBy(i => i));
282300

283301
private string FormatSymbol(MdSetup setup, ISymbol? symbol, FormatOptions options) =>
284302
symbol switch
@@ -453,31 +471,31 @@ public string ActualKind
453471
typeKind = "anonymous";
454472
}
455473

456-
if (Type.IsRecord)
474+
typeKind = Type.TypeKind switch
457475
{
458-
typeKind = "record";
459-
}
476+
TypeKind.Interface or TypeKind.Enum or TypeKind.Delegate or TypeKind.Struct => Type.TypeKind.ToString().ToLower(),
477+
_ => typeKind
478+
};
460479

461480
if (Type is IArrayTypeSymbol)
462481
{
463482
typeKind = "array";
464483
}
465484

466-
if (Type.IsTupleType)
485+
if (Type.IsAbstract && Type.TypeKind == TypeKind.Class)
467486
{
468-
typeKind = "tuple";
487+
typeKind = "abstract";
469488
}
470489

471-
if (Type.IsAbstract)
490+
if (Type.IsRecord)
472491
{
473-
typeKind = "abstract";
492+
typeKind = "record";
474493
}
475494

476-
typeKind = Type.TypeKind switch
495+
if (Type.IsTupleType)
477496
{
478-
TypeKind.Interface or TypeKind.Enum or TypeKind.Delegate or TypeKind.Struct => Type.TypeKind.ToString().ToLower(),
479-
_ => typeKind
480-
};
497+
typeKind = "tuple";
498+
}
481499

482500
return string.IsNullOrWhiteSpace(typeKind) ? Type.TypeKind.ToString().ToLower() : typeKind;
483501
}

0 commit comments

Comments
 (0)