Skip to content

Commit c2b35a1

Browse files
authored
Reorganize README and reduce duplication (#243)
* Update to use trusted publishing * Try to reduce duplication and differences in readmes * Run README generation * Add ToC * Reorder sections * Fix ToC generation * Fix Readme
1 parent 14abf3a commit c2b35a1

25 files changed

Lines changed: 1217 additions & 212 deletions

.config/dotnet-tools.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": 1,
3+
"isRoot": true,
4+
"tools": {
5+
"markdownsnippets.tool": {
6+
"version": "28.0.1",
7+
"commands": [
8+
"mdsnippets"
9+
],
10+
"rollForward": false
11+
}
12+
}
13+
}

.github/workflows/BuildAndPack.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
user: ${{ secrets.NUGET_USER || 'NOT_SET' }}
6363

6464
- name: Run './build.cmd Clean Test TestPackage PushToNuGet
65-
run: ./build.cmd Clean Test TestPackage PushToNuGet
65+
run: ./build.cmd Clean GenerateReadmes Test TestPackage PushToNuGet
6666
env:
6767
NuGetToken: ${{ steps.login.outputs.NUGET_API_KEY || 'NOT_SET'}}
6868

build/Build.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,14 @@ class Build : NukeBuild
194194
}
195195
});
196196

197+
Target GenerateReadmes => _ => _
198+
.Before(Pack)
199+
.Executes(() =>
200+
{
201+
DotNetToolRestore();
202+
DotNet("tool run mdsnippets", RootDirectory / "docs");
203+
});
204+
197205
Target PushToNuGet => _ => _
198206
.DependsOn(Pack)
199207
.OnlyWhenStatic(() => IsTag && IsServerBuild && IsWin)

src/NetEscapades.EnumGenerators.Generators/README.md renamed to docs/NetEscapades.EnumGenerators.Generators.md

Lines changed: 65 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
<!--
2+
GENERATED FILE - DO NOT EDIT
3+
This file was generated by [MarkdownSnippets](https://github.com/SimonCropp/MarkdownSnippets).
4+
Source File: /NetEscapades.EnumGenerators.Generators.source.md
5+
To change this file edit the source file and then run MarkdownSnippets.
6+
-->
7+
18
# ![](https://raw.githubusercontent.com/andrewlock/NetEscapades.EnumGenerators/refs/heads/main/docs/images/icon_32.png) NetEscapades.EnumGenerators.Generators
29

310
![Build status](https://github.com/andrewlock/NetEscapades.EnumGenerators/actions/workflows/BuildAndPack.yml/badge.svg)
@@ -9,9 +16,25 @@ In general, we recommend installing the [NetEscapades.EnumGenerators](https://ww
916

1017
> [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) requires the .NET 7 SDK or higher. [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) requires the .NET 8.0.400 SDK or higher. You can still target earlier frameworks like .NET Core 3.1 etc, the version requirement only applies to the version of the .NET SDK installed.
1118
19+
<!-- toc -->
20+
## Contents
21+
22+
* [Why use these packages?](#why-use-these-packages)
23+
* [Adding NetEscapades.EnumGenerators.Generators to your project](#adding-netescapadesenumgeneratorsgenerators-to-your-project)
24+
* [Controlling extension accessibility](#controlling-extension-accessibility)
25+
* [Usage Analyzers](#usage-analyzers)
26+
* [Enabling the analyzers](#enabling-the-analyzers)
27+
* [Configuring analyzer severity (optional)](#configuring-analyzer-severity-optional)
28+
* [Code fixes](#code-fixes)
29+
* [Package referencing options](#package-referencing-options)
30+
* [Avoiding runtime dependencies](#avoiding-runtime-dependencies)
31+
* [Choosing the correct packages for your scenario](#choosing-the-correct-packages-for-your-scenario)
32+
* [Enabling automatic interception (experimental)](#enabling-automatic-interception-experimental)
33+
* [Preserving usages of the `[EnumExtensions]` attribute](#preserving-usages-of-the-enumextensions-attribute)<!-- endToc -->
34+
1235
## Why use these packages?
1336

14-
Many methods that work with enums are surprisingly slow. Calling `ToString()` or `HasFlag()` on an enum seems like it _should_ be fast, but it often isn't. This package provides a set of extension methods, such as `ToStringFast()` or `HasFlagFast()` that are designed to be very fast, with fewer allocations.
37+
Many methods that work with enums are surprisingly slow. Calling `ToString()` or `HasFlag()` on an enum seems like it _should_ be fast, but it often isn't. This package provides a set of extension methods, such as `ToStringFast()` or `HasFlagFast()` that are designed to be very fast, with fewer allocations.<!-- include: benchmark. path: /fragments/benchmark.include.md -->
1538

1639

1740
For example, the following benchmark shows the advantage of calling `ToStringFast()` over `ToString()`:
@@ -44,25 +67,7 @@ public enum Color
4467
Blue = 1,
4568
}
4669
```
47-
48-
The main downside to the extension methods generated by [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) is that you have to remember to use them. The [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) package solves this problem by intercepting calls to `ToString()` and replacing them with calls `ToStringFast()` automatically using the .NET compiler feature called [interceptors](https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md).
49-
50-
51-
For example, imagine you have this code, which uses the `Color` enum defined above:
52-
53-
```csharp
54-
var choice = Color.Red;
55-
Console.WriteLine("You chose: " + choice.ToString());
56-
```
57-
58-
By default you need to manually replace these `ToString()` calls with `ToStringFast()`. However, when you use the [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors), the interceptor automatically replaces the call to `choice.ToString()` at compile-time with a call to `ToStringFast()`, as though you wrote the following:
59-
60-
```csharp
61-
// The compiler replaces the call with this 👇
62-
Console.WriteLine("You chose: " + choice.ToStringFast());
63-
```
64-
65-
There are many caveats to this behaviour, as described below, but any explicit calls to `ToString()` or `HasFlag()` on a supported enum are replaced automatically.
70+
<!-- endInclude -->
6671

6772
## Adding NetEscapades.EnumGenerators.Generators to your project
6873

@@ -85,14 +90,14 @@ This adds a `<PackageReference>` to your project. You can additionally mark the
8590
</PropertyGroup>
8691

8792
<!-- Add the package -->
88-
<PackageReference Include="NetEscapades.EnumGenerators.Generators" Version="1.0.0-beta20"
93+
<PackageReference Include="NetEscapades.EnumGenerators.Generators" Version="1.0.0-beta20"
8994
PrivateAssets="all" ExcludeAssets="runtime" />
9095
<!-- -->
9196

9297
</Project>
9398
```
9499

95-
Adding the package will automatically add a marker attribute, `[EnumExtensions]`, to your project.
100+
Adding the package will automatically add a marker attribute, `[EnumExtensions]`, to your project.<!-- include: enum-usage. path: /fragments/enum-usage.include.md -->
96101

97102
To use the generator, add the `[EnumExtensions]` attribute to an enum. For example:
98103

@@ -102,7 +107,7 @@ public enum MyEnum
102107
{
103108
First,
104109

105-
[Display(Name = "2nd")]
110+
[EnumMember(Value = "2nd")]
106111
Second,
107112
}
108113
```
@@ -160,7 +165,7 @@ public static partial class MyEnumExtensions
160165
return true;
161166
}
162167

163-
168+
164169
return name switch
165170
{
166171
nameof(MyEnum.First) => true,
@@ -181,7 +186,7 @@ public static partial class MyEnumExtensions
181186
public static bool TryParse(string? name, out MyEnum value)
182187
=> TryParse(name, out value, false, false);
183188

184-
public static bool TryParse(string? name, out MyEnum value, bool ignoreCase)
189+
public static bool TryParse(string? name, out MyEnum value, bool ignoreCase)
185190
=> TryParse(name, out value, ignoreCase, false);
186191

187192
public static bool TryParse(string? name, out MyEnum value, bool ignoreCase, bool allowMatchingMetadataAttribute)
@@ -331,13 +336,16 @@ You can also set the `IsInternal` property on individual enums using the `[EnumE
331336
[EnumExtensions(IsInternal = true)]
332337
public enum MyEnum { ... }
333338
```
339+
334340
or for external enums:
335341

336342
```csharp
337343
// Force ExternalEnum's extensions to be internal
338344
[EnumExtensions<ExternalEnum>(IsInternal = true)]
339345
```
346+
<!-- endInclude -->
340347

348+
<!-- include: usage-analyzers. path: /fragments/usage-analyzers.include.md -->
341349
## Usage Analyzers
342350

343351
_NetEscapades.EnumGenerators_ includes optional analyzers that encourage the use of the generated extension methods instead of the built-in `System.Enum` methods. These analyzers can help improve performance by suggesting the faster, generated, alternatives like `ToStringFast()`, `HasFlagFast()`, and `TryParse()`.
@@ -363,7 +371,7 @@ After using one of these configuration options, the analyzers in your project sh
363371

364372
### Configuring analyzer severity (optional)
365373

366-
Once enabled, you can optionally configure the severity of individual analyzer rules using one or more [`.editorconfig` files](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/configuration-files#editorconfig). For example, the following changes all the built-in analyzers to report usages as errors instead of warnings
374+
Once enabled, you can optionally configure the severity of individual analyzer rules using one or more [`.editorconfig` files](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/configuration-files#editorconfig). For example, the following changes all the built-in analyzers to report usages as errors instead of warnings
367375

368376
```ini
369377
[*.{cs,vb}]
@@ -406,8 +414,9 @@ Valid severity values include: `none`, `silent`, `suggestion`, `warning`, and `e
406414

407415
All usage analyzers include automatic code fixes. When a diagnostic is triggered, you can use the quick fix functionality in your IDE to automatically replace the `System.Enum` method with the corresponding generated extension method:
408416

409-
![Demonstrating the code-fix option available in your IDE for each of the analyzers](https://raw.githubusercontent.com/andrewlock/NetEscapades.EnumGenerators/refs/heads/main/docs/images//code_fix.png)
417+
![Demonstrating the code-fix option available in your IDE for each of the analyzers](https://raw.githubusercontent.com/andrewlock/NetEscapades.EnumGenerators/refs/heads/main/docs/images//code_fix.png)<!-- endInclude -->
410418

419+
<!-- include: package-referencing. path: /fragments/package-referencing.include.md -->
411420
## Package referencing options
412421

413422
[NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) is a metapackage that references additional packages for functionality.
@@ -460,12 +469,12 @@ namespace NetEscapades.EnumGenerators;
460469
public readonly struct EnumParseOptions { }
461470

462471
/// <summary>
463-
/// Options to apply when calling <c>ToStringFast</c> on an enum.
472+
/// Options to apply when calling <c>ToStringFast</c> on an enum.
464473
/// </summary>
465474
public readonly struct SerializationOptions
466475

467476
/// <summary>
468-
/// Transform to apply when calling <c>ToStringFast</c>
477+
/// Transform to apply when calling <c>ToStringFast</c>
469478
/// </summary>
470479
public enum SerializationTransform
471480
```
@@ -479,7 +488,7 @@ namespace SomeNameSpace;
479488
public static partial class MyEnumExtensions
480489
{
481490
// ... generated members
482-
491+
483492
// The runtime dependencies are generated as nested types instead
484493
public readonly struct EnumParseOptions { }
485494
public readonly struct SerializationOptions
@@ -538,9 +547,28 @@ The final option is to reference [NetEscapades.EnumGenerators.Generators](https:
538547
```
539548

540549
> [!WARNING]
541-
> When using the [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) metapackage, it's important you _don't_ set `PrivateAssets=All`. If you want to use `PrivateAssets=All`, use [NetEscapades.EnumGenerators.Generators](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Generators) for this scenario.
550+
> When using the [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) metapackage, it's important you _don't_ set `PrivateAssets=All`. If you want to use `PrivateAssets=All`, use [NetEscapades.EnumGenerators.Generators](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Generators) for this scenario.<!-- endInclude -->
551+
552+
## Enabling automatic interception (experimental)
553+
554+
The main downside to the extension methods generated by [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) is that you have to remember to use them. The [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) package solves this problem by intercepting calls to `ToString()` and replacing them with calls `ToStringFast()` automatically using the .NET compiler feature called [interceptors](https://github.com/dotnet/roslyn/blob/main/docs/features/interceptors.md).<!-- include: interceptor-intro. path: /fragments/interceptor-intro.include.md -->
555+
542556

543-
## Enabling automatic interception
557+
For example, imagine you have this code, which uses the `Color` enum defined above:
558+
559+
```csharp
560+
var choice = Color.Red;
561+
Console.WriteLine("You chose: " + choice.ToString());
562+
```
563+
564+
By default you need to manually replace these `ToString()` calls with `ToStringFast()`. However, when you use the [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors), the interceptor automatically replaces the call to `choice.ToString()` at compile-time with a call to `ToStringFast()`, as though you wrote the following:
565+
566+
```csharp
567+
// The compiler replaces the call with this 👇
568+
Console.WriteLine("You chose: " + choice.ToStringFast());
569+
```
570+
571+
There are many caveats to this behaviour, as described below, but any explicit calls to `ToString()` or `HasFlag()` on a supported enum are replaced automatically.<!-- endInclude -->
544572

545573
Interceptors were introduced as an experimental feature in C#12 with .NET 8. They allow a source generator to "intercept" certain method calls, and replace the call with a different one. _NetEscapades.EnumGenerators_ has support for intercepting `ToString()` and `HasFlag()` method calls.
546574

@@ -554,7 +582,7 @@ dotnet add package NetEscapades.EnumGenerators.Interceptors
554582

555583
This adds a `<PackageReference>` to your project. You can additionally mark the package as `PrivateAssets="all"` and `ExcludeAssets="runtime"`, similarly to _NetEscapades.EnumGenerators_.
556584

557-
By default, adding [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) to a project enables interception for all enums _defined in that project_ that use the `[EnumExtensions]` or `[EnumExtensions<T>]` attributes. If you wish to intercept calls made to enums with extensions defined in _other_ projects, you must add the `[Interceptable<T>]` attribute in the project where you want the interception to happen, e.g.
585+
By default, adding [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) to a project enables interception for all enums _defined in that project_ that use the `[EnumExtensions]` or `[EnumExtensions<T>]` attributes. If you wish to intercept calls made to enums with extensions defined in _other_ projects, you must add the `[Interceptable<T>]` attribute in the project where you want the interception to happen, e.g.<!-- include: interception-config. path: /fragments/interception-config.include.md -->
558586
559587
```csharp
560588
[assembly:Interceptable<DateTimeKind>]
@@ -598,7 +626,7 @@ public void CantIntercept()
598626
{
599627
var bad1 = ((System.Enum)Color.Red).ToString(); // ❌ Base type
600628
var bad2 = ((object)Color.Red).ToString(); // ❌ Base type
601-
629+
602630
var bad3 = "The colour is " + red; // ❌ implicit
603631
var bad4 = $"The colour is {red}"; // ❌ implicit
604632
@@ -609,7 +637,9 @@ public void CantIntercept()
609637
}
610638
}
611639
```
640+
<!-- endInclude -->
612641

642+
<!-- include: preserving-usages. path: /fragments/preserving-usages.include.md -->
613643
## Preserving usages of the `[EnumExtensions]` attribute
614644

615645
The `[EnumExtensions]` attribute is decorated with the `[Conditional]` attribute, [so their usage will not appear in the build output of your project](https://andrewlock.net/conditional-compilation-for-ignoring-method-calls-with-the-conditionalattribute/#applying-the-conditional-attribute-to-classes). If you use reflection at runtime on one of your `enum`s, you will not find `[EnumExtensions]` in the list of custom attributes. If you wish to preserve these attributes in the build output, you can define the `NETESCAPADES_ENUMGENERATORS_USAGES` MSBuild variable.
@@ -628,3 +658,4 @@ The `[EnumExtensions]` attribute is decorated with the `[Conditional]` attribute
628658
<PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta20" />
629659
</Project>
630660
```
661+
<!-- endInclude -->
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# ![](https://raw.githubusercontent.com/andrewlock/NetEscapades.EnumGenerators/refs/heads/main/docs/images/icon_32.png) NetEscapades.EnumGenerators.Generators
2+
3+
![Build status](https://github.com/andrewlock/NetEscapades.EnumGenerators/actions/workflows/BuildAndPack.yml/badge.svg)
4+
[![NuGet](https://img.shields.io/nuget/v/NetEscapades.EnumGenerators.svg)](https://www.nuget.org/packages/NetEscapades.EnumGenerators/)
5+
6+
[NetEscapades.EnumGenerators.Generators](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Generators) is a source generator package that generates extension methods for enums, to allow fast "reflection".
7+
8+
In general, we recommend installing the [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) metapackage. [See below](#package-referencing-options) for details on choosing between these two packages.
9+
10+
> [NetEscapades.EnumGenerators](https://www.nuget.org/packages/NetEscapades.EnumGenerators) requires the .NET 7 SDK or higher. [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) requires the .NET 8.0.400 SDK or higher. You can still target earlier frameworks like .NET Core 3.1 etc, the version requirement only applies to the version of the .NET SDK installed.
11+
12+
toc
13+
14+
## Why use these packages?
15+
16+
include: benchmark
17+
18+
## Adding NetEscapades.EnumGenerators.Generators to your project
19+
20+
Add the package to your application using
21+
22+
```bash
23+
dotnet add package NetEscapades.EnumGenerators.Generators
24+
```
25+
26+
This adds a `<PackageReference>` to your project. You can additionally mark the package as `PrivateAssets="all"` and `ExcludeAssets="runtime"`.
27+
28+
> Setting `PrivateAssets="all"` means any projects referencing this one won't get a reference to the _NetEscapades.EnumGenerators.Generators_ package. Setting `ExcludeAssets="runtime"` ensures the _NetEscapades.EnumGenerators.Attributes.dll_ file is not copied to your build output (it is not required at runtime).
29+
30+
```xml
31+
<Project Sdk="Microsoft.NET.Sdk">
32+
33+
<PropertyGroup>
34+
<OutputType>Exe</OutputType>
35+
<TargetFramework>net8.0</TargetFramework>
36+
</PropertyGroup>
37+
38+
<!-- Add the package -->
39+
<PackageReference Include="NetEscapades.EnumGenerators.Generators" Version="1.0.0-beta20"
40+
PrivateAssets="all" ExcludeAssets="runtime" />
41+
<!-- -->
42+
43+
</Project>
44+
```
45+
46+
include: enum-usage
47+
48+
include: usage-analyzers
49+
50+
include: package-referencing
51+
52+
## Enabling automatic interception (experimental)
53+
54+
include: interceptor-intro
55+
56+
Interceptors were introduced as an experimental feature in C#12 with .NET 8. They allow a source generator to "intercept" certain method calls, and replace the call with a different one. _NetEscapades.EnumGenerators_ has support for intercepting `ToString()` and `HasFlag()` method calls.
57+
58+
> To use interceptors, you must be using at least version 8.0.400 of the .NET SDK. [This ships with Visual Studio version 17.11](https://learn.microsoft.com/en-us/dotnet/core/porting/versioning-sdk-msbuild-vs), so you will need at least that version or higher.
59+
60+
To use interception, add the additional NuGet package [NetEscapades.EnumGenerators.Interceptors](https://www.nuget.org/packages/NetEscapades.EnumGenerators.Interceptors) to your project using:
61+
62+
```bash
63+
dotnet add package NetEscapades.EnumGenerators.Interceptors
64+
```
65+
66+
This adds a `<PackageReference>` to your project. You can additionally mark the package as `PrivateAssets="all"` and `ExcludeAssets="runtime"`, similarly to _NetEscapades.EnumGenerators_.
67+
68+
include: interception-config
69+
70+
include: preserving-usages

0 commit comments

Comments
 (0)