diff --git a/src/projects/EnsureThat/Enforcers/EnumArg.cs b/src/projects/EnsureThat/Enforcers/EnumArg.cs
index 2df4f1e..8c693f3 100644
--- a/src/projects/EnsureThat/Enforcers/EnumArg.cs
+++ b/src/projects/EnsureThat/Enforcers/EnumArg.cs
@@ -1,4 +1,5 @@
using System;
+using EnsureThat.Internals;
using JetBrains.Annotations;
namespace EnsureThat.Enforcers
@@ -7,14 +8,15 @@ public sealed class EnumArg
{
///
/// Confirms that the is defined in the enum .
- /// Note that just like `Enum.IsDefined`, `Flags` based enums may be valid combination of defined values, but if the combined value
- /// itself is not named an error will be raised. Avoid usage with `Flags` enums.
+ /// Note that just like ,
+ /// based enums may be valid combination of defined values, but if the combined value
+ /// itself is not named an error will be raised. Avoid usage with enums.
///
///
/// Flags example:
///
/// [Flags]
- /// enum Abc{
+ /// enum Abc {
/// A = 1,
/// B = 2,
/// C = 4,
@@ -22,7 +24,7 @@ public sealed class EnumArg
/// }
///
/// Abc.A | Abc.B IsDefined=true (due to Abc.AB)
- /// Abc.A | Abc.C IsDefined=false (A and C are both valid, the composite is valid due to `Flags` attribute, but the composite is not a named enum value
+ /// Abc.A | Abc.C IsDefined=false (A and C are both valid, the composite is valid due to attribute, but the composite is not a named enum value
///
public T IsDefined(T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : struct, Enum
{
@@ -37,5 +39,25 @@ public T IsDefined(T value, [InvokerParameterName] string paramName = null, O
return value;
}
+
+ ///
+ /// Confirms that the is defined in the enum .
+ /// Supports attribute.
+ ///
+ public T IsDefinedWithFlagsSupport(T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : struct, Enum
+ {
+ var isEnumDefined = EnumOf.Contains(value);
+
+ if (!isEnumDefined)
+ {
+ throw Ensure.ExceptionFactory.ArgumentOutOfRangeException(
+ string.Format(ExceptionMessages.Enum_IsValidEnum, value, EnumOf.EnumType),
+ paramName,
+ value,
+ optsFn);
+ }
+
+ return value;
+ }
}
}
\ No newline at end of file
diff --git a/src/projects/EnsureThat/Ensure.cs b/src/projects/EnsureThat/Ensure.cs
index 596c107..bc7a45a 100644
--- a/src/projects/EnsureThat/Ensure.cs
+++ b/src/projects/EnsureThat/Ensure.cs
@@ -22,7 +22,7 @@ public static class Ensure
public static BoolArg Bool { get; } = new BoolArg();
///
- /// Ensures for enumerables.
+ /// Ensures for enums.
///
[NotNull]
public static EnumArg Enum { get; } = new EnumArg();
diff --git a/src/projects/EnsureThat/EnsureArg.Enums.cs b/src/projects/EnsureThat/EnsureArg.Enums.cs
index 6a011f2..4337c7b 100644
--- a/src/projects/EnsureThat/EnsureArg.Enums.cs
+++ b/src/projects/EnsureThat/EnsureArg.Enums.cs
@@ -8,14 +8,15 @@ public static partial class EnsureArg
{
///
/// Confirms that the is defined in the enum .
- /// Note that just like `Enum.IsDefined`, `Flags` based enums may be valid combination of defined values, but if the combined value
- /// itself is not named an error will be raised. Avoid usage with `Flags` enums.
+ /// Note that just like ,
+ /// based enums may be valid combination of defined values, but if the combined value
+ /// itself is not named an error will be raised. Avoid usage with enums.
///
///
/// Flags example:
///
/// [Flags]
- /// enum Abc{
+ /// enum Abc {
/// A = 1,
/// B = 2,
/// C = 4,
@@ -23,9 +24,16 @@ public static partial class EnsureArg
/// }
///
/// Abc.A | Abc.B IsDefined=true (due to Abc.AB)
- /// Abc.A | Abc.C IsDefined=false (A and C are both valid, the composite is valid due to `Flags` attribute, but the composite is not a named enum value
+ /// Abc.A | Abc.C IsDefined=false (A and C are both valid, the composite is valid due to attribute, but the composite is not a named enum value
///
public static T EnumIsDefined(T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : struct, Enum
=> Ensure.Enum.IsDefined(value, paramName, optsFn);
+
+ ///
+ /// Confirms that the is defined in the enum .
+ /// Supports attribute.
+ ///
+ public static T EnumIsDefinedWithFlagsSupport(T value, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : struct, Enum
+ => Ensure.Enum.IsDefinedWithFlagsSupport(value, paramName, optsFn);
}
}
\ No newline at end of file
diff --git a/src/projects/EnsureThat/EnsureThatEnumExtensions.cs b/src/projects/EnsureThat/EnsureThatEnumExtensions.cs
index 47dd2c5..a56df1c 100644
--- a/src/projects/EnsureThat/EnsureThatEnumExtensions.cs
+++ b/src/projects/EnsureThat/EnsureThatEnumExtensions.cs
@@ -4,14 +4,15 @@ public static class EnsureThatEnumExtensions
{
///
/// Confirms that the is defined in the enum .
- /// Note that just like `Enum.IsDefined`, `Flags` based enums may be valid combination of defined values, but if the combined value
- /// itself is not named an error will be raised. Avoid usage with `Flags` enums.
+ /// Note that just like ,
+ /// based enums may be valid combination of defined values, but if the combined value
+ /// itself is not named an error will be raised. Avoid usage with enums.
///
///
/// Flags example:
///
/// [Flags]
- /// enum Abc{
+ /// enum Abc {
/// A = 1,
/// B = 2,
/// C = 4,
@@ -19,9 +20,16 @@ public static class EnsureThatEnumExtensions
/// }
///
/// Abc.A | Abc.B IsDefined=true (due to Abc.AB)
- /// Abc.A | Abc.C IsDefined=false (A and C are both valid, the composite is valid due to `Flags` attribute, but the composite is not a named enum value
+ /// Abc.A | Abc.C IsDefined=false (A and C are both valid, the composite is valid due to ` attribute, but the composite is not a named enum value
///
public static void IsDefined(this in Param param) where T : struct, System.Enum
=> Ensure.Enum.IsDefined(param.Value, param.Name, param.OptsFn);
+
+ ///
+ /// Confirms that the is defined in the enum .
+ /// Supports attribute.
+ ///
+ public static void IsDefinedWithFlagsSupport(this in Param param) where T : struct, System.Enum
+ => Ensure.Enum.IsDefinedWithFlagsSupport(param.Value, param.Name, param.OptsFn);
}
}
\ No newline at end of file
diff --git a/src/projects/EnsureThat/Internals/EnumOf.cs b/src/projects/EnsureThat/Internals/EnumOf.cs
new file mode 100644
index 0000000..8fba3d6
--- /dev/null
+++ b/src/projects/EnsureThat/Internals/EnumOf.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace EnsureThat.Internals
+{
+ internal static class EnumOf where T : struct, Enum
+ {
+ internal static readonly Type EnumType;
+ private static readonly bool _hasFlags;
+ private static readonly List _values;
+
+ static EnumOf()
+ {
+ EnumType = typeof(T);
+
+ _hasFlags = EnumType.GetTypeInfo().GetCustomAttributes(false).Any();
+
+ var enumValues = Enum.GetValues(EnumType);
+ _values = new List(enumValues.Length);
+ foreach (var v in enumValues)
+ _values.Add(Convert.ToUInt64(v));
+ }
+
+ internal static bool Contains(T value)
+ {
+ if (!_hasFlags)
+ return Enum.IsDefined(EnumType, value);
+
+ var raw = Convert.ToUInt64(value);
+ if (raw == 0)
+ return Enum.IsDefined(EnumType, value);
+
+ ulong sum = 0;
+
+ foreach (var val in _values)
+ {
+ if ((raw & val) == val)
+ sum |= val;
+ }
+
+ return sum == raw;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/tests/UnitTests/EnsureEnumParamTests.cs b/src/tests/UnitTests/EnsureEnumParamTests.cs
index 5fcc00a..909c65d 100644
--- a/src/tests/UnitTests/EnsureEnumParamTests.cs
+++ b/src/tests/UnitTests/EnsureEnumParamTests.cs
@@ -9,7 +9,53 @@ public class EnsureEnumParamTests : UnitTestBase
[Fact]
public void IsDefined_ShouldNotThrow()
{
- var item = Only1IsValid.Valid;
+ var item = Only1IsValidEnum.Valid;
+
+ ShouldNotThrow(
+ () => Ensure.Enum.IsDefined(item, ParamName),
+ () => EnsureArg.EnumIsDefined(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefined());
+ }
+
+ [Theory]
+ [InlineData((Only1IsValidEnum)2)]
+ [InlineData((Only1IsValidEnum)0)]
+ public void NotDefined_ShouldThrow(Only1IsValidEnum item)
+ {
+ ShouldThrow(
+ string.Format(ExceptionMessages.Enum_IsValidEnum, item, typeof(Only1IsValidEnum)),
+ () => Ensure.Enum.IsDefined(item, ParamName),
+ () => EnsureArg.EnumIsDefined(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefined());
+ }
+
+ [Fact]
+ public void IsDefinedWithFlagsSupport_ShouldNotThrow()
+ {
+ var item = Only1IsValidEnum.Valid;
+
+ ShouldNotThrow(
+ () => Ensure.Enum.IsDefinedWithFlagsSupport(item, ParamName),
+ () => EnsureArg.EnumIsDefinedWithFlagsSupport(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefinedWithFlagsSupport());
+ }
+
+ [Theory]
+ [InlineData((Only1IsValidEnum)2)]
+ [InlineData((Only1IsValidEnum)0)]
+ public void NotDefined_Extended_ShouldThrow(Only1IsValidEnum item)
+ {
+ ShouldThrow(
+ string.Format(ExceptionMessages.Enum_IsValidEnum, item, typeof(Only1IsValidEnum)),
+ () => Ensure.Enum.IsDefinedWithFlagsSupport(item, ParamName),
+ () => EnsureArg.EnumIsDefinedWithFlagsSupport(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefinedWithFlagsSupport());
+ }
+
+ [Fact]
+ public void FlagIsDefined_ShouldNotThrow_IfNotCombined()
+ {
+ var item = TestFlagsEnum.Bar;
ShouldNotThrow(
() => Ensure.Enum.IsDefined(item, ParamName),
@@ -18,20 +64,95 @@ public void IsDefined_ShouldNotThrow()
}
[Fact]
- public void IsNotDefined_ShouldThrow()
+ public void FlagIsDefined_ShouldThrow_IfCombined()
{
- var item = (Only1IsValid)2;
+ var item = TestFlagsEnum.Bar | TestFlagsEnum.Baz;
ShouldThrow(
- string.Format(ExceptionMessages.Enum_IsValidEnum, item, typeof(Only1IsValid)),
+ string.Format(ExceptionMessages.Enum_IsValidEnum, item, typeof(TestFlagsEnum)),
() => Ensure.Enum.IsDefined(item, ParamName),
() => EnsureArg.EnumIsDefined(item, ParamName),
() => Ensure.That(item, ParamName).IsDefined());
}
- private enum Only1IsValid
+ [Fact]
+ public void FlagNotDefined_ShouldThrow()
+ {
+ var item = (TestFlagsEnum)3;
+ ShouldThrow(
+ string.Format(ExceptionMessages.Enum_IsValidEnum, item, typeof(TestFlagsEnum)),
+ () => Ensure.Enum.IsDefined(item, ParamName),
+ () => EnsureArg.EnumIsDefined(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefined());
+ }
+
+ [Theory]
+ [InlineData(TestFlagsEnum.Bar)]
+ [InlineData(TestFlagsEnum.Bar | TestFlagsEnum.Baz)]
+ public void FlagIsDefined_Extended_ShouldNotThrow(TestFlagsEnum item)
+ {
+ ShouldNotThrow(
+ () => Ensure.Enum.IsDefinedWithFlagsSupport(item, ParamName),
+ () => EnsureArg.EnumIsDefinedWithFlagsSupport(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefinedWithFlagsSupport());
+ }
+
+ [Theory]
+ [InlineData((TestFlagsEnum)4)]
+ [InlineData((TestFlagsEnum)0)]
+ public void FlagNotDefined_Extended_ShouldThrow(TestFlagsEnum item)
+ {
+ ShouldThrow(
+ string.Format(ExceptionMessages.Enum_IsValidEnum, item, typeof(TestFlagsEnum)),
+ () => Ensure.Enum.IsDefinedWithFlagsSupport(item, ParamName),
+ () => EnsureArg.EnumIsDefinedWithFlagsSupport(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefinedWithFlagsSupport());
+ }
+
+ [Theory]
+ [InlineData(TestFlagsOfWhateverPower.A)]
+ [InlineData(TestFlagsOfWhateverPower.B)]
+ [InlineData(TestFlagsOfWhateverPower.C)]
+ [InlineData(TestFlagsOfWhateverPower.A | TestFlagsOfWhateverPower.C)]
+ [InlineData(TestFlagsOfWhateverPower.B | TestFlagsOfWhateverPower.C)]
+ public void FlagOfWhateverPowerIsDefined_ShouldNotThrow(TestFlagsOfWhateverPower item)
+ {
+ ShouldNotThrow(
+ () => Ensure.Enum.IsDefinedWithFlagsSupport(item, ParamName),
+ () => EnsureArg.EnumIsDefinedWithFlagsSupport(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefinedWithFlagsSupport());
+ }
+
+ [Fact]
+ public void FlagOfWhateverPowerIsNotDefined_ShouldThrow()
+ {
+ var item = (TestFlagsOfWhateverPower)9;
+
+ ShouldThrow(
+ string.Format(ExceptionMessages.Enum_IsValidEnum, item, typeof(TestFlagsOfWhateverPower)),
+ () => Ensure.Enum.IsDefinedWithFlagsSupport(item, ParamName),
+ () => EnsureArg.EnumIsDefinedWithFlagsSupport(item, ParamName),
+ () => Ensure.That(item, ParamName).IsDefinedWithFlagsSupport());
+ }
+
+ public enum Only1IsValidEnum : byte
{
Valid = 1
}
+
+ [Flags]
+ public enum TestFlagsEnum : byte
+ {
+ Bar = 1,
+ Baz = 1 << 1
+ }
+
+ [Flags]
+ public enum TestFlagsOfWhateverPower : byte
+ {
+ A = 4,
+ B = 5,
+ C = 8
+ }
}
}
\ No newline at end of file