diff --git a/src/Fluxera.ValueObject.EntityFrameworkCore/EntityTypeBuilderExtensions.cs b/src/Fluxera.ValueObject.EntityFrameworkCore/EntityTypeBuilderExtensions.cs index 88ea7f4..49679ca 100644 --- a/src/Fluxera.ValueObject.EntityFrameworkCore/EntityTypeBuilderExtensions.cs +++ b/src/Fluxera.ValueObject.EntityFrameworkCore/EntityTypeBuilderExtensions.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; - using Fluxera.Guards; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -23,7 +22,7 @@ public static class EntityTypeBuilderExtensions /// public static void UsePrimitiveValueObject(this EntityTypeBuilder entityTypeBuilder) { - Guard.Against.Null(entityTypeBuilder); + Guard.ThrowIfNull(entityTypeBuilder); IEnumerable properties = entityTypeBuilder.Metadata .ClrType diff --git a/src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj b/src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj index a70bf61..a08ad77 100644 --- a/src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj +++ b/src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj @@ -30,15 +30,15 @@ - + - + - + diff --git a/src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs b/src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs index d8aaa9b..8ed4507 100644 --- a/src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs +++ b/src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; - using Fluxera.Guards; using JetBrains.Annotations; using Newtonsoft.Json.Serialization; @@ -42,7 +41,7 @@ IEnumerator IEnumerable.GetEnumerator() /// public void Add(IContractResolver contractResolver) { - Guard.Against.Null(contractResolver); + Guard.ThrowIfNull(contractResolver); if(this.contractResolvers.Contains(this.defaultContractResolver)) { diff --git a/src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj b/src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj index 40967ab..dd42e1d 100644 --- a/src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj +++ b/src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj @@ -27,7 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs b/src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs index 3250680..660d377 100644 --- a/src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs +++ b/src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs @@ -13,6 +13,8 @@ public static class JsonSerializerSettingsExtensions /// public static void UsePrimitiveValueObject(this JsonSerializerSettings settings) { + Guard.ThrowIfNull(settings); + settings.ContractResolver = new CompositeContractResolver { new PrimitiveValueObjectContractResolver() diff --git a/src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs b/src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs index 5d8bf62..f4d03c9 100644 --- a/src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs +++ b/src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; - using Fluxera.Guards; using global::LiteDB; using JetBrains.Annotations; @@ -20,7 +19,7 @@ public static class BsonMapperExtensions /// public static BsonMapper UsePrimitiveValueObject(this BsonMapper mapper) { - Guard.Against.Null(mapper); + Guard.ThrowIfNull(mapper); IEnumerable primitiveValueObjectTypes = AppDomain.CurrentDomain .GetAssemblies() diff --git a/src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj b/src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj index 1e8d149..8ed4d99 100644 --- a/src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj +++ b/src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj @@ -27,7 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs b/src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs index 77257bc..04d757e 100644 --- a/src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs +++ b/src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs @@ -16,6 +16,8 @@ public static class ConventionPackExtensions /// public static ConventionPack UsePrimitiveValueObject(this ConventionPack pack) { + Guard.ThrowIfNull(pack); + pack.Add(new PrimitiveValueObjectConvention()); return pack; diff --git a/src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj b/src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj index c176e14..d22d71b 100644 --- a/src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj +++ b/src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj @@ -27,7 +27,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs b/src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs index 5e53040..5d0a925 100644 --- a/src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs +++ b/src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs @@ -15,6 +15,8 @@ public static class JsonSerializerOptionsExtensions /// public static void UsePrimitiveValueObject(this JsonSerializerOptions options) { + Guard.ThrowIfNull(options); + options.Converters.Add(new PrimitiveValueObjectJsonConverterFactory()); } } diff --git a/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj b/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj index c066861..27cb483 100644 --- a/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj +++ b/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj @@ -1,4 +1,4 @@ - + net6.0;net7.0;net8.0 @@ -22,8 +22,14 @@ - - + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Fluxera.ValueObject/Guard.cs b/src/Fluxera.ValueObject/Guard.cs new file mode 100644 index 0000000..e132e39 --- /dev/null +++ b/src/Fluxera.ValueObject/Guard.cs @@ -0,0 +1,122 @@ +namespace Fluxera.ValueObject +{ + using JetBrains.Annotations; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Numerics; + using System.Runtime.CompilerServices; + + internal static class Guard + { + public static T ThrowIfNull(T argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + ArgumentNullException.ThrowIfNull(argument, parameterName); + + return argument; + } + + public static string ThrowIfNullOrEmpty(string argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + if(string.IsNullOrEmpty(argument)) + { + throw new ArgumentException("Value cannot be empty.", parameterName); + } + + return argument; + } + + public static string ThrowIfNullOrWhiteSpace(string argument, [InvokerParameterName][CallerArgumentExpression("argument")] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + if(string.IsNullOrWhiteSpace(argument)) + { + throw new ArgumentException("Value cannot be whitespace-only.", parameterName); + } + + return argument; + } + + public static bool ThrowIfFalse(bool argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null, string message = null) + { + if(!argument) + { + throw new ArgumentException(message ?? "Value cannot be false.", parameterName); + } + + return true; + } + + public static IEnumerable ThrowIfNullOrEmpty(IEnumerable argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + argument = ThrowIfNull(argument, parameterName); + + // ReSharper disable PossibleMultipleEnumeration + if(!argument.Any()) + { + throw new ArgumentException("Enumerable cannot be empty.", parameterName); + } + + return argument; + // ReSharper enable PossibleMultipleEnumeration + } + +#if NET7_0_OR_GREATER + public static T ThrowIfNegative(T argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + where T : INumber + { + if(T.IsNegative(argument)) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } +#endif + +#if NET6_0 + public static byte ThrowIfNegative(byte argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static short ThrowIfNegative(short argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static int ThrowIfNegative(int argument, [InvokerParameterName] [CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } + + public static long ThrowIfNegative(long argument, [InvokerParameterName][CallerArgumentExpression(nameof(argument))] string parameterName = null) + { + if(argument < 0) + { + throw new ArgumentException("Value cannot be negative.", parameterName); + } + + return argument; + } +#endif + } +} diff --git a/src/Fluxera.ValueObject/PrimitiveValueObject.cs b/src/Fluxera.ValueObject/PrimitiveValueObject.cs index 0234ad5..60a65e8 100644 --- a/src/Fluxera.ValueObject/PrimitiveValueObject.cs +++ b/src/Fluxera.ValueObject/PrimitiveValueObject.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; - using Fluxera.Guards; - using Fluxera.Utilities.Extensions; using JetBrains.Annotations; /// @@ -29,7 +27,7 @@ static PrimitiveValueObject() Type valueType = typeof(TValue); bool isPrimitive = valueType.IsPrimitive(true); - Guard.Against.False(isPrimitive, nameof(Value), "The value of a primitive value object must be a primitive, string or enum value."); + Guard.ThrowIfFalse(isPrimitive, nameof(Value), "The value of a primitive value object must be a primitive, string or enum value."); } /// diff --git a/src/Fluxera.ValueObject/PrimitiveValueObjectConverter.cs b/src/Fluxera.ValueObject/PrimitiveValueObjectConverter.cs index b02ea52..c420b1a 100644 --- a/src/Fluxera.ValueObject/PrimitiveValueObjectConverter.cs +++ b/src/Fluxera.ValueObject/PrimitiveValueObjectConverter.cs @@ -4,7 +4,6 @@ using System.Collections.Concurrent; using System.ComponentModel; using System.Globalization; - using Fluxera.Guards; internal sealed class PrimitiveValueObjectConverter : TypeConverter { @@ -106,7 +105,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c /// public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { - Guard.Against.Null(value); + Guard.ThrowIfNull(value); PrimitiveValueObject primitiveValueObject = (PrimitiveValueObject)value; diff --git a/src/Fluxera.ValueObject/PropertyAccessor.cs b/src/Fluxera.ValueObject/PropertyAccessor.cs index 5a7eab2..d28aa38 100644 --- a/src/Fluxera.ValueObject/PropertyAccessor.cs +++ b/src/Fluxera.ValueObject/PropertyAccessor.cs @@ -4,7 +4,6 @@ using System.Collections.Concurrent; using System.Linq; using System.Reflection; - using Fluxera.Guards; using JetBrains.Annotations; [PublicAPI] @@ -16,8 +15,8 @@ internal sealed class PropertyAccessor private PropertyAccessor(string propertyName, Func getterFunc) { - Guard.Against.NullOrWhiteSpace(propertyName, nameof(propertyName)); - Guard.Against.Null(getterFunc, nameof(getterFunc)); + Guard.ThrowIfNullOrWhiteSpace(propertyName); + Guard.ThrowIfNull(getterFunc); this.PropertyName = propertyName; this.GetterFunc = getterFunc; diff --git a/src/Fluxera.ValueObject/TypeExtensions.cs b/src/Fluxera.ValueObject/TypeExtensions.cs new file mode 100644 index 0000000..8ae2f10 --- /dev/null +++ b/src/Fluxera.ValueObject/TypeExtensions.cs @@ -0,0 +1,56 @@ +namespace Fluxera.ValueObject +{ + using System; + using System.Collections.Generic; + + internal static class TypeExtensions + { + private static readonly HashSet ExtraPrimitiveTypes = + [ + typeof(string), + typeof(decimal), + typeof(DateOnly), + typeof(TimeOnly), + typeof(DateTime), + typeof(DateTimeOffset), + typeof(TimeSpan), + typeof(Guid) + ]; + + /// + /// Determines whether the specified type is a primitive. It automatically + /// unwraps the wrapped type of the nullable. + /// + /// The type. + /// If set to true include enums. + /// + /// true if the specified type is a primitive; otherwise, false. + /// + public static bool IsPrimitive(this Type type, bool includeEnums = false) + { + type = type.UnwrapNullableType(); + + if(type.IsPrimitive) + { + return true; + } + + if(includeEnums && type.IsEnum) + { + return true; + } + + return ExtraPrimitiveTypes.Contains(type); + } + + /// + /// Gets the type without nullable if the type is a . + /// + /// The type. + /// + private static Type UnwrapNullableType(this Type type) + { + return Nullable.GetUnderlyingType(type) ?? type; + } + } +}