Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/nuget/Enums.NET-5.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ardalis authored Sep 30, 2024
2 parents 1078085 + 935b494 commit e842602
Show file tree
Hide file tree
Showing 15 changed files with 499 additions and 34 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/publish-guardclauses.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: publish SmartEnum.GuardClauses to nuget
on:
workflow_dispatch:
push:
branches:
- main # Your default release branch
paths:
- 'src/SmartEnum.GuardClauses/**'
jobs:
publish:
name: list SmartEnum.GuardClauses on nuget.org
runs-on: windows-latest
steps:
- uses: actions/checkout@v2

# Required for a specific dotnet version that doesn't come with ubuntu-latest / windows-latest
# Visit bit.ly/2synnZl to see the list of SDKs that are pre-installed with ubuntu-latest / windows-latest
- name: Setup .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
# Publish
- name: publish on version change
uses: alirezanet/[email protected]
with:
PROJECT_FILE_PATH: src/SmartEnum.GuardClauses/SmartEnum.GuardClauses.csproj # Relative to repository root
VERSION_FILE_PATH: Directory.Build.props # Filepath with version info, relative to repository root. Defaults to project file
VERSION_REGEX: <Version>(.*)<\/Version> # Regex pattern to extract version info in a capturing group
TAG_COMMIT: true # Flag to enable / disable git tagging
TAG_FORMAT: SmartEnumGuardClauses-v* # Format of the git tag, [*] gets replaced with version
NUGET_KEY: ${{secrets.NUGET_API_KEY}} # nuget.org API key
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<PackageProjectUrl>https://github.com/ardalis/SmartEnum</PackageProjectUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<TargetFrameworks>net7.0;net6.0;net8.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>net7.0;net6.0;net8.0;netstandard2.0</TargetFrameworks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Version>8.0.0</Version>
Expand All @@ -35,4 +35,4 @@
up $(IsStableBuild) automatically; property is also used to control prerelease branding.
-->
</PropertyGroup>
</Project>
</Project>
21 changes: 11 additions & 10 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.GuardClauses" Version="4.5.0" />
<PackageVersion Include="AutoFixture" Version="4.18.1" />
<PackageVersion Include="BenchmarkDotNet" Version="0.13.10" />
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Include="Constant" Version="2.0.4" />
<PackageVersion Include="coverlet.collector" Version="3.2.0" />
<PackageVersion Include="coverlet.msbuild" Version="3.2.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
<PackageVersion Include="Dapper" Version="2.1.28" />
<PackageVersion Include="Enums.NET" Version="5.0.0" />
<PackageVersion Include="FluentAssertions" Version="6.11.0" />
<PackageVersion Include="MessagePack" Version="2.4.59" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="MessagePack" Version="2.5.140" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="7.0.13" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="7.0.13" />
Expand All @@ -20,16 +21,16 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.2" />
<PackageVersion Include="protobuf-net" Version="3.2.26" />
<PackageVersion Include="protobuf-net" Version="3.2.30" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.12.0.78982" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.2" />
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
<PackageVersion Include="Utf8Json" Version="1.3.7" />
<PackageVersion Include="xunit" Version="2.7.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.1" />
</ItemGroup>

<ItemGroup>
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="9.17.0.82934" PrivateAssets="All" />
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="9.17.0.82934" PrivateAssets="All" />
</ItemGroup>
</Project>
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* [Persisting with EF Core 2.1 or higher](#persisting-with-ef-core-21-or-higher)
* [Using SmartEnum.EFCore](#using-smartenumefcore)
* [AutoFixture support](#autofixture-support)
* [Json.NET support](#jsonnet-support)
* [Json support](#jsonnet-support)
* [Dapper support](#dapper-support)
* [DapperSmartEnum](#dappersmartenum)
* [Case Insensitive String Enum](#case-insensitive-string-enum)
Expand Down Expand Up @@ -79,6 +79,7 @@ To install support for serialization, AutoFixture, EF Core, Model Binding, or Da
```
Install-Package Ardalis.SmartEnum.AutoFixture
Install-Package Ardalis.SmartEnum.JsonNet
Install-Package Ardalis.SmartEnum.SystemTextJson
Install-Package Ardalis.SmartEnum.Utf8Json
Install-Package Ardalis.SmartEnum.MessagePack
Install-Package Ardalis.SmartEnum.ProtoBufNet
Expand All @@ -89,13 +90,7 @@ Install-Package Ardalis.SmartEnum.Dapper

## Version

The latest version of the package supports .NET 7. If you don't need or aren't yet ready to move to .NET 7 or later, you should install the previous stable version, [Ardalis.SmartEnum 2.1](https://www.nuget.org/packages/Ardalis.SmartEnum/2.1.0).

Example package manager command:

```
Install-Package Ardalis.SmartEnum -Version 2.1.0
```
The latest version of the package supports .NET 8 and NetStandard 2.0.

## Usage

Expand Down Expand Up @@ -210,7 +205,7 @@ public class Manager
{
if (!ManagerType.TryFromName(value, true, out var parsed))
{
throw new Exception($"Invalid manage type of '{value}'");
throw new Exception($"Invalid manager type of '{value}'");
}
_managerType = parsed;
}
Expand Down Expand Up @@ -397,7 +392,7 @@ testEnumVar
.Default( ... );
```

N.B. For performance critical code the fluent interface carries some overhead that you may wish to avoid. See the available [benchmarks](src/SmartEnum.Benchmarks) code for your use case.
N.B. For performance critical code the fluent interface carries some overhead that you may wish to avoid. See the available [benchmarks](benchmarks/SmartEnum.Benchmarks) code for your use case.

### SmartFlagEnum

Expand Down Expand Up @@ -714,9 +709,15 @@ var fixture = new Fixture()
var smartEnum = fixture.Create<TestEnum>();
```

## Json<span></span>.NET support
## Json support

When serializing a `SmartEnum` to JSON, only one of the properties (`Value` or `Name`) should be used.

### Json<span></span>.Net
[Json.NET](https://www.newtonsoft.com/json) by default doesn't know how to do this. The `Ardalis.SmartEnum.JsonNet` package includes a couple of converters to achieve this. Simply use the attribute [JsonConverterAttribute](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonConverter.htm) to assign one of the converters to the `SmartEnum` to be de/serialized:

When serializing a `SmartEnum` to JSON, only one of the properties (`Value` or `Name`) should be used. [Json.NET](https://www.newtonsoft.com/json) by default doesn't know how to do this. The `Ardalis.SmartEnum.JsonNet` package includes a couple of converters to achieve this. Simply use the attribute [JsonConverterAttribute](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonConverter.htm) to assign one of the converters to the `SmartEnum` to be de/serialized:
### System<span></span>.Text<span></span>.Json
[System.Text.Json](https://learn.microsoft.com/en-us/dotnet/api/system.text.json?view=net-8.0) by default doesn't know how to do this. The `Ardalis.SmartEnum.SystemTextJson` package includes a couple of converters to achieve this. Simply use the attribute [JsonConverterAttribute](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverterattribute?view=net-8.0) to assign one of the converters to the `SmartEnum` to be de/serialized:

```csharp
public class TestClass
Expand Down
14 changes: 14 additions & 0 deletions SmartEnum.sln
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartEnum.Dapper.UnitTests"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartEnum.Dapper.IntegrationTests", "test\SmartEnum.Dapper.IntegrationTests\SmartEnum.Dapper.IntegrationTests.csproj", "{ACCA93E9-EE80-490C-81A3-824086E4EA2F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartEnum.GuardClauses", "src\SmartEnum.GuardClauses\SmartEnum.GuardClauses.csproj", "{A720F348-2176-4A47-ADC5-CC2664FDA516}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmartEnum.GuardClauses.UnitTests", "test\SmartEnum.GuardClauses.UnitTests\SmartEnum.GuardClauses.UnitTests.csproj", "{B7B944B3-E9DC-4CFC-BADC-11EC2F226AA2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -162,6 +166,14 @@ Global
{ACCA93E9-EE80-490C-81A3-824086E4EA2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACCA93E9-EE80-490C-81A3-824086E4EA2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACCA93E9-EE80-490C-81A3-824086E4EA2F}.Release|Any CPU.Build.0 = Release|Any CPU
{A720F348-2176-4A47-ADC5-CC2664FDA516}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A720F348-2176-4A47-ADC5-CC2664FDA516}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A720F348-2176-4A47-ADC5-CC2664FDA516}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A720F348-2176-4A47-ADC5-CC2664FDA516}.Release|Any CPU.Build.0 = Release|Any CPU
{B7B944B3-E9DC-4CFC-BADC-11EC2F226AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7B944B3-E9DC-4CFC-BADC-11EC2F226AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7B944B3-E9DC-4CFC-BADC-11EC2F226AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7B944B3-E9DC-4CFC-BADC-11EC2F226AA2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -190,6 +202,8 @@ Global
{7E08FCFA-2318-4D36-BAB5-8AFA41F00CC3} = {FA199ECB-5F29-442A-AAC6-91DBCB7A5A04}
{ADBD5097-87A4-492B-9399-6A4CCC53CD5A} = {79268877-BBEF-4DE2-B8D9-697F21933159}
{ACCA93E9-EE80-490C-81A3-824086E4EA2F} = {EF5634F4-4667-4481-934C-D1CFA042AD0B}
{A720F348-2176-4A47-ADC5-CC2664FDA516} = {FA199ECB-5F29-442A-AAC6-91DBCB7A5A04}
{B7B944B3-E9DC-4CFC-BADC-11EC2F226AA2} = {79268877-BBEF-4DE2-B8D9-697F21933159}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46896DE3-41B8-442F-A6FB-6AC9F11CCBCE}
Expand Down
23 changes: 16 additions & 7 deletions benchmarks/SmartEnum.Benchmarks/FromNameBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,27 @@ public class FromNameBenchmarks
////////////////////////////////////////////////////////////////////////////////
// Enum

private static TestEnum ParseTestEnum(string value, bool ignoreCase = false)
{
#if NETSTANDARD2_0
return (TestEnum)Enum.Parse(typeof(TestEnum), value, ignoreCase);
#else
return Enum.Parse<TestEnum>(value, ignoreCase);
#endif
}

[Benchmark]
public TestEnum Enum_FromName_One() => Enum.Parse<TestEnum>("One");
public TestEnum Enum_FromName_One() => ParseTestEnum("One");

[Benchmark]
public TestEnum Enum_FromName_Ten() => Enum.Parse<TestEnum>("Ten");
public TestEnum Enum_FromName_Ten() => ParseTestEnum("Ten");

[Benchmark]
public TestEnum Enum_FromName_Invalid()
{
try
{
return Enum.Parse<TestEnum>("Invalid");
return ParseTestEnum("Invalid");
}
catch (Exception)
{
Expand All @@ -32,18 +41,18 @@ public TestEnum Enum_FromName_Invalid()
}

[Benchmark]
public TestEnum Enum_FromName_one_IgnoreCase() => Enum.Parse<TestEnum>("one", true);
public TestEnum Enum_FromName_one_IgnoreCase() => ParseTestEnum("one", true);

[Benchmark]
public TestEnum Enum_FromName_ten_IgnoreCase() => Enum.Parse<TestEnum>("ten", true);
public TestEnum Enum_FromName_ten_IgnoreCase() => ParseTestEnum("ten", true);


[Benchmark]
public TestEnum Enum_FromName_Invalid_IgnoreCase()
{
try
{
return Enum.Parse<TestEnum>("Invalid", true);
return ParseTestEnum("Invalid", true);
}
catch (Exception)
{
Expand Down Expand Up @@ -229,4 +238,4 @@ public TestSmartEnum SmartEnum_TryFromName_Invalid_IgnoreCase()
}

}
}
}
66 changes: 66 additions & 0 deletions src/SmartEnum.GuardClauses/GuardAgainstSmartEnumOutOfRange.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Ardalis.GuardClauses;
using System;

namespace Ardalis.SmartEnum.GuardClauses
{
/// <summary>
/// Provides guard clauses to ensure input values are valid instances of a specified SmartEnum.
/// </summary>
public static class GuardAgainstSmartEnumOutOfRange
{
/// <summary>
/// Throws a<see cref="SmartEnumNotFoundException" /> or a custom<see cref="Exception" />
/// if <paramref name="input"/> is not a valid <see cref="SmartEnum{TEnum}"/> value.
/// </summary>
/// <typeparam name="TEnum">The type of the smart enum.</typeparam>
/// <param name="guardClause">The guard clause interface.</param>
/// <param name="input">The value to check against the smart enum values.</param>
/// <param name="message">Optional. Custom error message to pass to <see cref="SmartEnumNotFoundException"/>.</param>
/// <param name="exceptionCreator">Optional. A function that creates a custom exception.</param>
/// <returns>The valid <see cref="SmartEnum{TEnum}"/> value <paramref name="input" />.</returns>
/// <exception cref="SmartEnumNotFoundException">Thrown when <paramref name="input" />
/// is not a valid enum value, and no custom exception is provided.</exception>
/// <exception cref="Exception">Thrown when a custom exception is provided by <paramref name="exceptionCreator" />.</exception>
public static TEnum SmartEnumOutOfRange<TEnum>(
this IGuardClause guardClause,
int input,
string message = null,
Func<Exception> exceptionCreator = null)
where TEnum : SmartEnum<TEnum>
{
return guardClause.SmartEnumOutOfRange<TEnum, int>(input, message, exceptionCreator);
}

/// <summary>
/// Throws a <see cref="SmartEnumNotFoundException"/> or a custom <see cref="Exception"/>
/// if <paramref name="input"/> is not a valid <see cref="SmartEnum{TEnum, TValue}"/> value.
/// </summary>
/// <typeparam name="TEnum">The type of the smart enum.</typeparam>
/// <typeparam name="TValue">The type of the value that the smart enum uses.</typeparam>
/// <param name="guardClause">The guard clause interface.</param>
/// <param name="input">The value to check against the smart enum values.</param>
/// <param name="message">Optional. Custom error message to pass to <see cref="SmartEnumNotFoundException"/>.</param>
/// <param name="exceptionCreator">Optional. A function that creates a custom exception.</param>
/// <returns>The valid enum value <typeparamref name="TEnum"/> corresponding to <paramref name="input"/>.</returns>
/// <exception cref="SmartEnumNotFoundException">Thrown when <paramref name="input"/>
/// is not a valid enum value and no custom exception is provided.</exception>
/// <exception cref="Exception">Thrown when a custom exception
/// is provided by <paramref name="exceptionCreator"/>.</exception>
public static TEnum SmartEnumOutOfRange<TEnum, TValue>(
this IGuardClause guardClause,
TValue input,
string message = null,
Func<Exception> exceptionCreator = null)
where TEnum : SmartEnum<TEnum, TValue>
where TValue : IEquatable<TValue>, IComparable<TValue>
{
if (SmartEnum<TEnum, TValue>.TryFromValue(input, out TEnum result))
{
return result;
}

var exceptionMessage = message ?? $"The value '{input}' is not a valid {typeof(TEnum).Name}.";
throw exceptionCreator?.Invoke() ?? new SmartEnumNotFoundException(exceptionMessage);
}
}
}
Loading

0 comments on commit e842602

Please sign in to comment.