Skip to content

Commit

Permalink
Improved examples
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Dec 23, 2024
1 parent 35568e5 commit 391282a
Show file tree
Hide file tree
Showing 218 changed files with 4,776 additions and 13,235 deletions.
1 change: 1 addition & 0 deletions Pure.DI.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=Rewriter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=rolsyn/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=seealso/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=shouldly/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Shroedingers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Singleline/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=typeparam/@EntryIndexedValue">True</s:Boolean>
Expand Down
120 changes: 82 additions & 38 deletions build/ReadmeTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ private static async Task AddContentAsync(string sourceFile, TextWriter readmeWr

private async Task<IEnumerable<(string GroupName, Dictionary<string, string>[] SampleItems)>> CreateExamplesAsync(CancellationToken cancellationToken)
{
var solutionDir = env.GetPath(PathType.SolutionDirectory);
var items = new List<Dictionary<string, string>>();
var testsDir = Path.Combine(env.GetPath(PathType.SolutionDirectory), "tests", "Pure.DI.UsageTests");
var testsDir = Path.Combine(solutionDir, "tests", "Pure.DI.UsageTests");
var files = Directory.EnumerateFiles(testsDir, "*.cs", SearchOption.AllDirectories);
foreach (var file in files)
{
Expand Down Expand Up @@ -243,6 +244,7 @@ private static async Task AddContentAsync(string sourceFile, TextWriter readmeWr

private async Task GenerateExamplesAsync(IEnumerable<(string GroupName, Dictionary<string, string>[] SampleItems)> examples, TextWriter readmeWriter, string logsDirectory)
{
var solutionDir = env.GetPath(PathType.SolutionDirectory);
var generatorPackageVersion = versions.GetNext(new NuGetRestoreSettings("Pure.DI"), Settings.VersionRange, 0).ToString();
var msPackageVersion = versions.GetNext(new NuGetRestoreSettings("Pure.DI.MS"), Settings.VersionRange, 0).ToString();
foreach (var readmeFile in Directory.EnumerateFiles(Path.Combine(ReadmeDir), "*.md"))
Expand All @@ -267,55 +269,96 @@ private async Task GenerateExamplesAsync(IEnumerable<(string GroupName, Dictiona

await readmeWriter.WriteLineAsync("## Examples");
await readmeWriter.WriteLineAsync("");
foreach (var (groupName, exampleItems) in examples)

var tempDir = Path.Combine(env.GetPath(PathType.TempDirectory));
Directory.CreateDirectory(tempDir);
try
{
var groupTitle = new string(FormatTitle(groupName).ToArray());
Info($"Processing examples group \"{groupTitle}\"");
await readmeWriter.WriteLineAsync($"### {groupTitle}");
foreach (var vars in exampleItems)
new DotNetNew()
.WithWorkingDirectory(tempDir)
.WithTemplateName("console")
.WithName("App")
.Run().EnsureSuccess();

var appDir = Path.Combine(tempDir, "App");
File.Copy(Path.Combine(solutionDir, "tests", "Pure.DI.UsageTests", "Test.props"), Path.Combine(appDir, "Directory.Build.props"));
var programFile = Path.Combine(appDir, "Program.cs");
foreach (var (groupName, exampleItems) in examples)
{
var description = vars[DescriptionKey];
var exampleFile = $"{CreateExampleFileName(description)}.md";
await using var examplesWriter = File.CreateText(Path.Combine(ReadmeDir, exampleFile));
WriteLine($" · \"{description}\"", Color.Details);
await readmeWriter.WriteLineAsync($"- [{description}]({ReadmeDir}/{exampleFile})");
await examplesWriter.WriteLineAsync($"#### {description}");
await examplesWriter.WriteLineAsync("");
await examplesWriter.WriteLineAsync($"[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../{vars[SourceKey].Replace('\\', '/')})");
await examplesWriter.WriteLineAsync("");
var header = vars[HeaderKey];
if (!string.IsNullOrWhiteSpace(header))
var groupTitle = new string(FormatTitle(groupName).ToArray());
Info($"Processing examples group \"{groupTitle}\"");
await readmeWriter.WriteLineAsync($"### {groupTitle}");
foreach (var vars in exampleItems)
{
await examplesWriter.WriteLineAsync(header);
await examplesWriter.WriteLineAsync("");
}
var description = vars[DescriptionKey];
var code = vars[BodyKey];
var file = CreateExampleFileName(description);
await File.WriteAllTextAsync(programFile, code);
var result = new DotNetBuild()
.WithProject(Path.Combine(appDir, "App.csproj"))
.WithWorkingDirectory(solutionDir)
.WithProps(("SolutionDir", solutionDir))
.Build();
if (result.ExitCode != 0)
{
WriteLine($"{file}:", Color.Header);
foreach (var line in code.Split([Environment.NewLine], StringSplitOptions.None))
{
WriteLine(line, Color.Details);
}

await examplesWriter.WriteLineAsync("");
await examplesWriter.WriteLineAsync("```c#");
await examplesWriter.WriteLineAsync(vars[BodyKey]);
await examplesWriter.WriteLineAsync("```");
await examplesWriter.WriteLineAsync("");
continue;
}

var footer = vars[FooterKey];
if (!string.IsNullOrWhiteSpace(footer))
{
await examplesWriter.WriteLineAsync(footer);
var exampleFile = $"{file}.md";
await using var examplesWriter = File.CreateText(Path.Combine(ReadmeDir, exampleFile));
WriteLine(description, Color.Details);
await readmeWriter.WriteLineAsync($"- [{description}]({ReadmeDir}/{exampleFile})");
await examplesWriter.WriteLineAsync($"#### {description}");
await examplesWriter.WriteLineAsync("");
}
await examplesWriter.WriteLineAsync($"[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../{vars[SourceKey].Replace('\\', '/')})");
await examplesWriter.WriteLineAsync("");
var header = vars[HeaderKey];
if (!string.IsNullOrWhiteSpace(header))
{
await examplesWriter.WriteLineAsync(header);
await examplesWriter.WriteLineAsync("");
}

var exampleName = Path.GetFileNameWithoutExtension(vars[SourceKey]);
await examplesWriter.WriteLineAsync("");
await examplesWriter.WriteLineAsync("```c#");
await examplesWriter.WriteLineAsync(code);
await examplesWriter.WriteLineAsync("```");
await examplesWriter.WriteLineAsync("");

await AddExample(logsDirectory, $"Pure.DI.UsageTests.*.{exampleName}.*.g.cs", examplesWriter);
await examplesWriter.WriteLineAsync("");
var footer = vars[FooterKey];
if (!string.IsNullOrWhiteSpace(footer))
{
await examplesWriter.WriteLineAsync(footer);
await examplesWriter.WriteLineAsync("");
}

await AddClassDiagram(logsDirectory, exampleName, examplesWriter);
await examplesWriter.WriteLineAsync("");
var exampleName = Path.GetFileNameWithoutExtension(vars[SourceKey]);

await AddExample(logsDirectory, $"Pure.DI.UsageTests.*.{exampleName}.*.g.cs", examplesWriter);
await examplesWriter.WriteLineAsync("");

await examplesWriter.FlushAsync();
await AddClassDiagram(logsDirectory, exampleName, examplesWriter);
await examplesWriter.WriteLineAsync("");

await examplesWriter.FlushAsync();
}
}
}
finally
{
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
}
}

private static async Task AddClassDiagram(string logsDirectory, string exampleName, TextWriter examplesWriter)
{
var classDiagramFile = Path.Combine(logsDirectory, exampleName + ".Mermaid");
Expand Down Expand Up @@ -376,12 +419,13 @@ private static async Task AddExample(string logsDirectory, string exampleSearchP
.Replace("Pure.DI.", "")
.Replace("Benchmarks.Model.", "")
.Replace(salt, "")));

await examplesWriter.WriteLineAsync(generatedCode);
await examplesWriter.WriteLineAsync("```");
}
}

private static async Task AddBenchmarksAsync(string logsDirectory, TextWriter readmeWriter)
private async Task AddBenchmarksAsync(string logsDirectory, TextWriter readmeWriter)
{
var benchmarksReportFiles = Directory.EnumerateFiles(logsDirectory, "*.html").ToArray();
if (benchmarksReportFiles.Length != 0)
Expand Down
150 changes: 5 additions & 145 deletions readme/a-few-partial-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ The setting code for one Composition can be located in several methods and/or in


```c#
using Pure.DI;

var composition = new Composition();
var service = composition.Root;

interface IDependency;

class Dependency : IDependency;
Expand Down Expand Up @@ -37,153 +42,8 @@ partial class Composition
DI.Setup()
.Root<IService>("Root");
}

var composition = new Composition();
var service = composition.Root;
```

The following partial class will be generated:

```c#
partial class Composition
{
private readonly Composition _root;

[OrdinalAttribute(256)]
public Composition()
{
_root = this;
}

internal Composition(Composition parentScope)
{
_root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root;
}

public IService Root
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return new Service(new Dependency());
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Resolve<T>()
{
return Resolver<T>.Value.Resolve(this);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T Resolve<T>(object? tag)
{
return Resolver<T>.Value.ResolveByTag(this, tag);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object Resolve(Type type)
{
var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1));
ref var pair = ref _buckets[index];
return pair.Key == type ? pair.Value.Resolve(this) : Resolve(type, index);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private object Resolve(Type type, int index)
{
var finish = index + _bucketSize;
while (++index < finish)
{
ref var pair = ref _buckets[index];
if (pair.Key == type)
{
return pair.Value.Resolve(this);
}
}

throw new InvalidOperationException($"{CannotResolveMessage} {OfTypeMessage} {type}.");
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object Resolve(Type type, object? tag)
{
var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1));
ref var pair = ref _buckets[index];
return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : Resolve(type, tag, index);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private object Resolve(Type type, object? tag, int index)
{
var finish = index + _bucketSize;
while (++index < finish)
{
ref var pair = ref _buckets[index];
if (pair.Key == type)
{
return pair.Value.ResolveByTag(this, tag);
}
}

throw new InvalidOperationException($"{CannotResolveMessage} \"{tag}\" {OfTypeMessage} {type}.");
}

private readonly static int _bucketSize;
private readonly static Pair<Type, IResolver<Composition, object>>[] _buckets;

static Composition()
{
var valResolver_0000 = new Resolver_0000();
Resolver<IService>.Value = valResolver_0000;
_buckets = Buckets<Type, IResolver<Composition, object>>.Create(
1,
out _bucketSize,
new Pair<Type, IResolver<Composition, object>>[1]
{
new Pair<Type, IResolver<Composition, object>>(typeof(IService), valResolver_0000)
});
}

private const string CannotResolveMessage = "Cannot resolve composition root ";
private const string OfTypeMessage = "of type ";

private class Resolver<T>: IResolver<Composition, T>
{
public static IResolver<Composition, T> Value = new Resolver<T>();

public virtual T Resolve(Composition composite)
{
throw new InvalidOperationException($"{CannotResolveMessage}{OfTypeMessage}{typeof(T)}.");
}

public virtual T ResolveByTag(Composition composite, object tag)
{
throw new InvalidOperationException($"{CannotResolveMessage}\"{tag}\" {OfTypeMessage}{typeof(T)}.");
}
}

private sealed class Resolver_0000: Resolver<IService>
{
public override IService Resolve(Composition composition)
{
return composition.Root;
}

public override IService ResolveByTag(Composition composition, object tag)
{
switch (tag)
{
case null:
return composition.Root;

default:
return base.ResolveByTag(composition, tag);
}
}
}
}
```

Class diagram:

Expand Down
Loading

0 comments on commit 391282a

Please sign in to comment.