From 9154eb132f4ab12b0e883edc76a873ca88cee2e5 Mon Sep 17 00:00:00 2001 From: Samuel Abraham Date: Sun, 12 Nov 2023 11:12:14 -0800 Subject: [PATCH] v7.4.1 --- .../Extensions/GraphQLExtensions.cs | 2 +- .../Resolvers/PropertyFieldResolver.cs | 24 +- .../TypeCache.GraphQL.csproj | 2 +- .../Types/GraphQLEnumType.cs | 18 +- .../Types/GraphQLNumberType.cs | 2 +- .../Types/GraphQLStringType.cs | 2 +- .../Web/GraphQLMiddleware.cs | 4 +- .../EndpointRouteBuilderExtensions.cs | 162 ++++++++++++ .../Extensions/HttpContextExtensions.cs | 11 +- src/TypeCache.Web/TypeCache.Web.csproj | 2 +- src/TypeCache/Collections/CustomComparer.cs | 16 +- .../Collections/CustomEqualityComparer.cs | 8 +- src/TypeCache/Collections/CustomEquatable.cs | 25 +- src/TypeCache/Collections/Delegates.cs | 7 - src/TypeCache/Collections/EnumComparer.cs | 32 +-- .../Converters/StringValuesJsonConverter.cs | 42 +++ .../Converters/ValueJsonConverter.cs | 2 + src/TypeCache/Extensions/ArrayExtensions.cs | 3 + src/TypeCache/Extensions/AssertExtensions.cs | 5 +- src/TypeCache/Extensions/EnumExtensions.cs | 32 ++- .../Extensions/EnumerableExtensions.cs | 4 +- .../Extensions/ExpressionExtensions.cs | 2 +- .../Extensions/ReflectionExtensions.Type.cs | 14 +- src/TypeCache/Extensions/SpanExtensions.cs | 2 +- .../Net/Mediation/HttpClientValidationRule.cs | 11 +- src/TypeCache/TypeCache.csproj | 2 +- src/TypeCache/Utilities/Enum.cs | 70 +++++ src/TypeCache/Utilities/EnumOf.cs | 35 --- src/TypeCache/Utilities/EventOf.cs | 1 + src/TypeCache/Utilities/HashMaker.cs | 51 ++-- src/TypeCache/Utilities/IHashMaker.cs | 14 +- src/TypeCache/Utilities/LambdaFactory.cs | 182 ++++++------- src/TypeCache/Utilities/RegexCache.cs | 17 +- src/TypeCache/Utilities/Token.cs | 29 +- src/TypeCache/Utilities/TypeStore.cs | 6 +- src/TypeCache/Utilities/ValueConverter.cs | 250 +++++++++++------- .../TypeCache.GraphQL.TestApp.csproj | 2 +- .../Extensions/EnumExtensions.cs | 3 +- tests/TypeCache.Tests/TypeCache.Tests.csproj | 8 +- .../TypeCache.Tests/Utilities/EnumOfTests.cs | 39 --- tests/TypeCache.Tests/Utilities/EnumTests.cs | 33 +++ 41 files changed, 714 insertions(+), 462 deletions(-) delete mode 100644 src/TypeCache/Collections/Delegates.cs create mode 100644 src/TypeCache/Converters/StringValuesJsonConverter.cs create mode 100644 src/TypeCache/Utilities/Enum.cs delete mode 100644 src/TypeCache/Utilities/EnumOf.cs delete mode 100644 tests/TypeCache.Tests/Utilities/EnumOfTests.cs create mode 100644 tests/TypeCache.Tests/Utilities/EnumTests.cs diff --git a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs index fb5f67a8..329fd971 100644 --- a/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs +++ b/src/TypeCache.GraphQL/Extensions/GraphQLExtensions.cs @@ -132,7 +132,7 @@ public static Type ToGraphQLType(this Type @this, bool isInputType) ? @this.GenericTypeArguments.First()!.ToGraphQLType(false) : throw new ArgumentOutOfRangeException(nameof(@this), Invariant($"{nameof(Task)} and {nameof(ValueTask)} are not allowed as GraphQL types.")); - var scalarGraphType = @this.GetDataType().ToGraphType(); + var scalarGraphType = @this.GetScalarType().ToGraphType(); if (scalarGraphType is not null) return scalarGraphType; diff --git a/src/TypeCache.GraphQL/Resolvers/PropertyFieldResolver.cs b/src/TypeCache.GraphQL/Resolvers/PropertyFieldResolver.cs index 9ec757b2..544da892 100644 --- a/src/TypeCache.GraphQL/Resolvers/PropertyFieldResolver.cs +++ b/src/TypeCache.GraphQL/Resolvers/PropertyFieldResolver.cs @@ -2,12 +2,13 @@ using System; using System.Reflection; +using System.Text.RegularExpressions; using System.Threading.Tasks; using GraphQL; using TypeCache.Extensions; -using TypeCache.Utilities; using static System.FormattableString; using static System.Globalization.CultureInfo; +using static System.Text.RegularExpressions.RegexOptions; namespace TypeCache.GraphQL.Resolvers; @@ -57,17 +58,19 @@ public PropertyFieldResolver(PropertyInfo propertyInfo) dateTime = dateTime.ChangeTimeZone(currentTimeZone, timeZone!); } - value = format.IsNotBlank() ? dateTime.ToString(format, InvariantCulture) : dateTime; + return format.IsNotBlank() ? dateTime.ToString(format, InvariantCulture) : dateTime; } - else if (value is DateTimeOffset dateTimeOffset) + + if (value is DateTimeOffset dateTimeOffset) { var timeZone = context.GetArgument("timeZone"); if (timeZone.IsNotBlank()) dateTimeOffset = dateTimeOffset.ToTimeZone(timeZone); - value = format.IsNotBlank() ? dateTimeOffset.ToString(format, InvariantCulture) : dateTimeOffset; + return format.IsNotBlank() ? dateTimeOffset.ToString(format, InvariantCulture) : dateTimeOffset; } - else if (value is string text) + + if (value is string text) { var trim = context.GetArgument("trim"); if (trim is not null) @@ -84,7 +87,7 @@ public PropertyFieldResolver(PropertyInfo propertyInfo) var pattern = context.GetArgument("match"); if (pattern.IsNotBlank()) { - var match = RegexCache.SinglelinePattern(pattern).Match(text); + var match = Regex.Match(text, pattern, Compiled | Singleline); if (match.Success) text = match.Value; else @@ -95,7 +98,7 @@ public PropertyFieldResolver(PropertyInfo propertyInfo) if (text.Length > length) text = text.Left(length.Value); - text = context.GetArgument("case") switch + return context.GetArgument("case") switch { StringCase.Lower => text.ToLower(), StringCase.LowerInvariant => text.ToLowerInvariant(), @@ -103,11 +106,10 @@ public PropertyFieldResolver(PropertyInfo propertyInfo) StringCase.UpperInvariant => text.ToUpperInvariant(), _ => text }; - - value = text; } - else if (format.IsNotBlank()) - value = string.Format(InvariantCulture, Invariant($"{{0:{format}}}"), value); + + if (format.IsNotBlank()) + return string.Format(InvariantCulture, Invariant($"{{0:{format}}}"), value); return value; } diff --git a/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj b/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj index dfbedadf..64a1866d 100644 --- a/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj +++ b/src/TypeCache.GraphQL/TypeCache.GraphQL.csproj @@ -4,7 +4,7 @@ enable TypeCache.GraphQL TypeCache.GraphQL - 7.4.0 + 7.4.1 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache GraphQL diff --git a/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs b/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs index b6490c39..85993465 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLEnumType.cs @@ -22,20 +22,20 @@ public GraphQLEnumType() this.Description = typeof(T).GraphQLDescription(); this.DeprecationReason = typeof(T).GraphQLDeprecationReason(); - var changeEnumCase = EnumOf.Attributes switch + var changeEnumCase = Enum.Tokens switch { - _ when EnumOf.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, - _ when EnumOf.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, - _ when EnumOf.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, + _ when Enum.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, + _ when Enum.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, + _ when Enum.Attributes.TryFirst(out var attribute) => attribute.ChangeEnumCase, _ => new Func(_ => _) }; - EnumOf.Tokens.Values - .Where(token => !token.Attributes.Any()) - .Select(token => new EnumValueDefinition(token.Attributes.FirstOrDefault()?.Name ?? changeEnumCase(token.Name), token.Value) + Enum.Tokens + .Where(_ => !_.Attributes.Any()) + .Select(_ => new EnumValueDefinition(_.Attributes.FirstOrDefault()?.Name ?? changeEnumCase(_.Name), _.Value) { - Description = token.Attributes.FirstOrDefault()?.Description, - DeprecationReason = token.Attributes.FirstOrDefault()?.DeprecationReason + Description = _.Attributes.FirstOrDefault()?.Description, + DeprecationReason = _.Attributes.FirstOrDefault()?.DeprecationReason }) .ToArray() .ForEach(this.Add); diff --git a/src/TypeCache.GraphQL/Types/GraphQLNumberType.cs b/src/TypeCache.GraphQL/Types/GraphQLNumberType.cs index 4dcb2ede..d5cd2653 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLNumberType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLNumberType.cs @@ -15,7 +15,7 @@ public GraphQLNumberType() : base( typeof(T).Name, value => T.TryParse(value.Value.Span, CultureInfo.InvariantCulture, out _), value => T.Parse(value.Value.Span, CultureInfo.InvariantCulture), - value => typeof(T).GetDataType() switch + value => typeof(T).GetScalarType() switch { ScalarType.SByte => ValueConverter.ConvertToSByte(value), ScalarType.Int16 => ValueConverter.ConvertToInt16(value), diff --git a/src/TypeCache.GraphQL/Types/GraphQLStringType.cs b/src/TypeCache.GraphQL/Types/GraphQLStringType.cs index 05c22c6b..5f4ef076 100644 --- a/src/TypeCache.GraphQL/Types/GraphQLStringType.cs +++ b/src/TypeCache.GraphQL/Types/GraphQLStringType.cs @@ -26,7 +26,7 @@ public GraphQLStringType() : base( typeof(T).Name, value => T.TryParse(value.Value.Span, CultureInfo.InvariantCulture, out _), value => T.Parse(value.Value.Span, CultureInfo.InvariantCulture), - value => typeof(T).GetDataType() switch + value => typeof(T).GetScalarType() switch { ScalarType.Char => ValueConverter.ConvertToChar(value), ScalarType.DateOnly => ValueConverter.ConvertToDateOnly(value), diff --git a/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs b/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs index 1c29ca06..a01f2768 100644 --- a/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs +++ b/src/TypeCache.GraphQL/Web/GraphQLMiddleware.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Threading.Tasks; using GraphQL; using GraphQL.DI; @@ -12,7 +11,6 @@ using GraphQL.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -using TypeCache.Extensions; using static System.FormattableString; using static System.Net.Mime.MediaTypeNames; using static System.StringSplitOptions; @@ -93,7 +91,7 @@ public async Task Invoke(HttpContext httpContext } httpContext.Response.ContentType = Application.Json; - httpContext.Response.StatusCode = (int)HttpStatusCode.OK; + httpContext.Response.StatusCode = StatusCodes.Status200OK; await graphQLSerializer.WriteAsync(httpContext.Response.Body, result, httpContext.RequestAborted); } } diff --git a/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs b/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs index 630b764d..8c764ba9 100644 --- a/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs +++ b/src/TypeCache.Web/Extensions/EndpointRouteBuilderExtensions.cs @@ -1,17 +1,167 @@ // Copyright (c) 2021 Samuel Abraham +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Xml.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Primitives; +using TypeCache.Converters; +using TypeCache.Extensions; using TypeCache.Web.Filters; using TypeCache.Web.Handlers; using static System.FormattableString; +using static System.Net.Mime.MediaTypeNames; namespace TypeCache.Web.Extensions; public static class EndpointRouteBuilderExtensions { + /// + /// /request/headers

+ /// Maps a GET endpoint that returns all request header values.
+ /// Supported formats (Accept header): + /// + /// application/json + /// application/xml + /// text/plain + /// text/html + /// text/xml + /// + ///
+ public static IEndpointConventionBuilder MapGetRequestHeaders(this IEndpointRouteBuilder @this) + => @this.MapGet("/request/headers", async context => + { + var acceptRequestHeader = context.Request.Headers.Accept.ToString(); + context.Response.StatusCode = context.Request.Headers.Any() ? StatusCodes.Status200OK : StatusCodes.Status204NoContent; + context.Response.Headers.ContentType = acceptRequestHeader switch + { + Text.Plain or Text.Html or Application.Xml or Text.Xml or Application.Json => acceptRequestHeader, + _ => Application.Json + }; + var response = string.Empty; + if (acceptRequestHeader.Equals(Text.Plain, StringComparison.OrdinalIgnoreCase)) + { + var responseBuilder = new StringBuilder(); + foreach (var pair in context.Request.Headers) + { + responseBuilder.Append(Invariant($"{pair.Key}: {pair.Value.ToString()}
")); + } + + response = responseBuilder.ToString(); + } + else if (acceptRequestHeader.Equals(Text.Html, StringComparison.OrdinalIgnoreCase)) + { + var table = new XElement("table", + new XElement("tr", + new XElement("th", "Header"), + new XElement("th", "Value"))); + foreach (var pair in context.Request.Headers) + { + table.Add(new XElement("tr", + new XElement("td", pair.Key), + new XElement("td", pair.Value.ToString()))); + } + + response = table.ToString(); + } + else if (acceptRequestHeader.Equals(Application.Xml, StringComparison.OrdinalIgnoreCase) + || acceptRequestHeader.Equals(Text.Xml, StringComparison.OrdinalIgnoreCase)) + { + var headers = new XElement("headers"); + foreach (var pair in context.Request.Headers) + { + headers.Add(new XElement(pair.Key.Replace(' ', '_'), pair.Value.ToString())); + } + + response = new XDocument(headers).ToString(); + } + else + { + response = JsonSerializer.Serialize(context.Request.Headers, CreateJsonSerializerOptions()); + } + + await context.Response.WriteAsync(response, context.RequestAborted); + }); + + /// + /// /request/headers/{key}

+ /// Maps a GET endpoint that returns the request header value for the specified parameter .
+ /// If no parameter is specified, then the route will handle any key.
+ /// Supported formats (Accept header): + /// + /// application/json + /// application/xml + /// text/plain + /// text/html + /// text/xml + /// + ///
+ public static IEndpointConventionBuilder MapGetRequestHeaderValue(this IEndpointRouteBuilder @this, string? key = null) + => @this.MapGet(key.IsNotBlank() ? Invariant($"/request/headers/{key}") : "/request/headers/{key}", async context => + { + key ??= context.GetRouteValue(nameof(key))!.ToString()!; + + var response = string.Empty; + var acceptRequestHeader = context.Request.Headers.Accept.ToString().ToLowerInvariant(); + context.Response.StatusCode = context.Request.Headers.TryGetValue(key, out var value) ? StatusCodes.Status200OK : StatusCodes.Status204NoContent; + context.Response.Headers.ContentType = acceptRequestHeader switch + { + Text.Plain or Text.Html or Application.Xml or Text.Xml or Application.Json => acceptRequestHeader, + _ => Application.Json + }; + response = (context.Response.StatusCode, acceptRequestHeader) switch + { + (StatusCodes.Status200OK, Text.Plain) => Invariant($"{key}: {value.ToString()}"), + (StatusCodes.Status204NoContent, Text.Plain) => Invariant($"{key}: "), + (StatusCodes.Status200OK, Text.Html) => Invariant($"

{key}


{value.ToString()}"), + (StatusCodes.Status204NoContent, Text.Html) => Invariant($"

{key}


"), + (StatusCodes.Status200OK, Application.Xml or Text.Xml) => new XDocument(new XElement(key.Replace(' ', '_'), value.ToString())).ToString(), + (StatusCodes.Status204NoContent, Application.Xml or Text.Xml) => new XDocument(new XElement(key.Replace(' ', '_'))).ToString(), + (StatusCodes.Status200OK, _) => JsonSerializer.Serialize(new Dictionary(1) { { key, value.ToString() } }, CreateJsonSerializerOptions()), + _ => JsonSerializer.Serialize(new Dictionary(1) { { key, null } }, CreateJsonSerializerOptions()) + }; + + await context.Response.WriteAsync(response, context.RequestAborted); + }); + + /// + /// /ping/{}

+ /// Maps a GET endpoint that calls a simple GET endpoint registered with to test connectivity.
+ /// Headers are optionally propagated using the Microsoft.AspNetCore.HeaderPropagation package.
+ /// Uses as a relative and propagates all query parameters.
+ /// An example use of this would be to ping another system's endpoint to ensure that the firewall allows the call to be made, + /// eliminating the need to install curl in the container or pod. + ///
+ public static IEndpointConventionBuilder MapGetPing(this IEndpointRouteBuilder @this, string name, Uri requestUri) + => @this.MapGet(Invariant($"/ping/{name}"), async (HttpContext context, IHttpClientFactory factory) => + { + var httpClient = factory.CreateClient(name); + + using var responseMessage = await httpClient.GetAsync(requestUri, HttpCompletionOption.ResponseContentRead, context.RequestAborted); + context.Response.StatusCode = (int)responseMessage.StatusCode; + foreach (var pair in responseMessage.Headers) + { + context.Response.Headers[pair.Key] = new StringValues(pair.Value?.ToArray()); + } + + if (context.Response.SupportsTrailers()) + { + foreach (var pair in responseMessage.TrailingHeaders) + { + context.Response.AppendTrailer(pair.Key, new StringValues(pair.Value?.ToArray())); + } + } + + await responseMessage.Content.CopyToAsync(context.Response.Body, context.RequestAborted); + }); + /// /// Endpoints that return composed SQL.
/// @@ -328,4 +478,16 @@ public static RouteHandlerBuilder MapSqlApiUpdateBatch(this IEndpointRouteBuilde => @this.MapPut(Invariant($"/{{dataSource:string}}/table/{{database:string}}/{{schema:string}}/{{table:string}}/batch"), SqlApiHandler.UpdateTableBatch) .AddEndpointFilter() .WithName("Batch Update"); + + private static JsonSerializerOptions CreateJsonSerializerOptions() + { + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + options.Converters.Add(new StringValuesJsonConverter()); + return options; + } } diff --git a/src/TypeCache.Web/Extensions/HttpContextExtensions.cs b/src/TypeCache.Web/Extensions/HttpContextExtensions.cs index 80f1d27a..6053197c 100644 --- a/src/TypeCache.Web/Extensions/HttpContextExtensions.cs +++ b/src/TypeCache.Web/Extensions/HttpContextExtensions.cs @@ -2,7 +2,6 @@ using System.IO; using System.Text.Json; -using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using static System.Net.Mime.MediaTypeNames; @@ -11,20 +10,20 @@ namespace TypeCache.Web.Extensions; public static class HttpContextExtensions { - public static async ValueTask GetJsonRequestAsync(this HttpContext @this, JsonSerializerOptions? options = null, CancellationToken token = default) - => await JsonSerializer.DeserializeAsync(@this.Request.Body, options, token); + public static async ValueTask GetJsonRequestAsync(this HttpContext @this, JsonSerializerOptions? options = null) + => await JsonSerializer.DeserializeAsync(@this.Request.Body, options, @this.RequestAborted); public static async ValueTask GetRequestAsync(this HttpContext @this) { using var reader = new StreamReader(@this.Request.Body); - return await reader.ReadToEndAsync(); + return await reader.ReadToEndAsync(@this.RequestAborted); } - public static async ValueTask WriteJsonResponseAsync(this HttpContext @this, T response, JsonSerializerOptions? options = null, CancellationToken token = default) + public static async ValueTask WriteJsonResponseAsync(this HttpContext @this, T response, JsonSerializerOptions? options = null) { @this.Response.ContentType = Application.Json; @this.Response.StatusCode = StatusCodes.Status200OK; - await JsonSerializer.SerializeAsync(@this.Response.Body, response, options, token); + await JsonSerializer.SerializeAsync(@this.Response.Body, response, options, @this.RequestAborted); } public static async ValueTask WriteResponseAsync(this HttpContext @this, string response) diff --git a/src/TypeCache.Web/TypeCache.Web.csproj b/src/TypeCache.Web/TypeCache.Web.csproj index 9718852d..ada39965 100644 --- a/src/TypeCache.Web/TypeCache.Web.csproj +++ b/src/TypeCache.Web/TypeCache.Web.csproj @@ -5,7 +5,7 @@ enable TypeCache.Web TypeCache.Web - 7.3.13 + 7.4.1 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Web Library diff --git a/src/TypeCache/Collections/CustomComparer.cs b/src/TypeCache/Collections/CustomComparer.cs index 35e047ee..9317b825 100644 --- a/src/TypeCache/Collections/CustomComparer.cs +++ b/src/TypeCache/Collections/CustomComparer.cs @@ -8,21 +8,25 @@ namespace TypeCache.Collections; private readonly Func _Equals; private readonly Func _GetHashCode; - public CustomComparer(Comparison compare, Func equals, Func getHashCode) + public CustomComparer(Comparison compare) { this._Compare = compare; - this._Equals = equals; - this._GetHashCode = getHashCode; + this._Equals = (x, y) => compare(x, y) == 0; + this._GetHashCode = _ => _?.GetHashCode() ?? 0; } public CustomComparer(Comparison compare, Func equals) - : this(compare, equals, _ => _?.GetHashCode() ?? 0) { + this._Compare = compare; + this._Equals = equals; + this._GetHashCode = _ => _?.GetHashCode() ?? 0; } - public CustomComparer(Comparison compare) - : this(compare, (x, y) => compare(x, y) == 0, _ => _?.GetHashCode() ?? 0) + public CustomComparer(Comparison compare, Func equals, Func getHashCode) { + this._Compare = compare; + this._Equals = equals; + this._GetHashCode = getHashCode; } [MethodImpl(AggressiveInlining), DebuggerHidden] diff --git a/src/TypeCache/Collections/CustomEqualityComparer.cs b/src/TypeCache/Collections/CustomEqualityComparer.cs index 8a7a02dc..b4557bf9 100644 --- a/src/TypeCache/Collections/CustomEqualityComparer.cs +++ b/src/TypeCache/Collections/CustomEqualityComparer.cs @@ -7,14 +7,16 @@ namespace TypeCache.Collections; private readonly Func _Equals; private readonly Func _GetHashCode; - public CustomEqualityComparer(Func equals, Func getHashCode) + public CustomEqualityComparer(Func equals) { this._Equals = equals; - this._GetHashCode = getHashCode; + this._GetHashCode = _ => _?.GetHashCode() ?? 0; } - public CustomEqualityComparer(Func equals) : this(equals, _ => _?.GetHashCode() ?? 0) + public CustomEqualityComparer(Func equals, Func getHashCode) { + this._Equals = equals; + this._GetHashCode = getHashCode; } [MethodImpl(AggressiveInlining), DebuggerHidden] diff --git a/src/TypeCache/Collections/CustomEquatable.cs b/src/TypeCache/Collections/CustomEquatable.cs index d82f4c24..f9fe624d 100644 --- a/src/TypeCache/Collections/CustomEquatable.cs +++ b/src/TypeCache/Collections/CustomEquatable.cs @@ -1,6 +1,7 @@ // Copyright (c) 2021 Samuel Abraham using TypeCache.Extensions; +using static System.Reflection.BindingFlags; namespace TypeCache.Collections; @@ -8,18 +9,12 @@ public abstract class CustomEquatable : IEquatable where T : class { private readonly Func _Equals; - private readonly int _HashCode; - public CustomEquatable(Func customEquatableEquals, params object[] customEqualityFactors) + public CustomEquatable(Func equals) { - customEquatableEquals.AssertNotNull(); - customEqualityFactors.AssertNotEmpty(); + equals.AssertNotNull(); - this._Equals = customEquatableEquals; - - var hashCode = new HashCode(); - customEqualityFactors.ForEach(hashCode.Add); - this._HashCode = hashCode.ToHashCode(); + this._Equals = equals; } [MethodImpl(AggressiveInlining), DebuggerHidden] @@ -30,7 +25,15 @@ public bool Equals(T? other) public override bool Equals(object? other) => this._Equals(other as T); - [MethodImpl(AggressiveInlining), DebuggerHidden] public override int GetHashCode() - => this._HashCode; + { + var fieldInfos = this.GetType().GetFields(DeclaredOnly | Instance | NonPublic | Public); + if (fieldInfos.Length == 0) + return base.GetHashCode(); + + var values = fieldInfos.Select(fieldInfo => fieldInfo.GetFieldValue(this)).ToArray(); + var hashCode = new HashCode(); + values.ForEach(hashCode.Add); + return hashCode.ToHashCode(); + } } diff --git a/src/TypeCache/Collections/Delegates.cs b/src/TypeCache/Collections/Delegates.cs deleted file mode 100644 index 4a4eca5d..00000000 --- a/src/TypeCache/Collections/Delegates.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -namespace TypeCache.Collections; - -public delegate void ActionRef(ref T item); -public delegate void ActionIndexRef(ref T item, int index); -public delegate ref T FunctionRef(); diff --git a/src/TypeCache/Collections/EnumComparer.cs b/src/TypeCache/Collections/EnumComparer.cs index 023949c1..3c7278e1 100644 --- a/src/TypeCache/Collections/EnumComparer.cs +++ b/src/TypeCache/Collections/EnumComparer.cs @@ -8,39 +8,39 @@ namespace TypeCache.Collections; public readonly struct EnumComparer : IComparer, IEqualityComparer where T : struct, Enum { - private readonly Comparison _Compare; - private readonly Func _Equals; - private readonly Func _GetHashCode; + private static readonly Comparison EnumCompare; + private static readonly Func EnumEquals; + private static readonly Func EnumGetHashCode; + + static EnumComparer() + { + var underlyingType = typeof(T).GetEnumUnderlyingType(); + EnumCompare = CreateCompare(underlyingType); + EnumEquals = CreateEquals(underlyingType); + EnumGetHashCode = CreateGetHashCode(underlyingType); + } private static Comparison CreateCompare(Type underlyingType) => LambdaFactory.CreateComparison((value1, value2) => - value1.Cast(underlyingType).Call(nameof(IComparable.CompareTo), value2.Convert(underlyingType))).Compile(); + value1.Cast(underlyingType).Call(nameof(IComparable.CompareTo), value2.Cast(underlyingType))).Compile(); private static Func CreateEquals(Type underlyingType) => LambdaFactory.CreateFunc((value1, value2) => - value1.Cast(underlyingType).Operation(BinaryOperator.EqualTo, value2.Convert(underlyingType))).Compile(); + value1.Cast(underlyingType).Operation(BinaryOperator.EqualTo, value2.Cast(underlyingType))).Compile(); private static Func CreateGetHashCode(Type underlyingType) => LambdaFactory.CreateFunc(value => value.Cast(underlyingType).Call(nameof(object.GetHashCode))).Compile(); - public EnumComparer() - { - var underlyingType = typeof(T).GetEnumUnderlyingType(); - this._Compare = CreateCompare(underlyingType); - this._Equals = CreateEquals(underlyingType); - this._GetHashCode = CreateGetHashCode(underlyingType); - } - [MethodImpl(AggressiveInlining), DebuggerHidden] public int Compare([AllowNull] T x, [AllowNull] T y) - => this._Compare(x, y); + => EnumCompare(x, y); [MethodImpl(AggressiveInlining), DebuggerHidden] public bool Equals([AllowNull] T x, [AllowNull] T y) - => this._Equals(x, y); + => EnumEquals(x, y); [MethodImpl(AggressiveInlining), DebuggerHidden] public int GetHashCode([DisallowNull] T value) - => this._GetHashCode(value); + => EnumGetHashCode(value); } diff --git a/src/TypeCache/Converters/StringValuesJsonConverter.cs b/src/TypeCache/Converters/StringValuesJsonConverter.cs new file mode 100644 index 00000000..ea9b4a07 --- /dev/null +++ b/src/TypeCache/Converters/StringValuesJsonConverter.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2021 Samuel Abraham + +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Extensions.Primitives; +using TypeCache.Extensions; + +namespace TypeCache.Converters; + +public sealed class StringValuesJsonConverter : JsonConverter +{ + public override StringValues Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType is JsonTokenType.StartArray) + { + var list = new List(0); + while (reader.Read() && reader.TokenType is not JsonTokenType.EndArray) + list.Add(reader.GetString()); + + return new(list.ToArray()); + } + + return new(reader.GetString()); + } + + public override void Write(Utf8JsonWriter writer, StringValues value, JsonSerializerOptions options) + { + if (value.Count > 1) + { + writer.WriteStartArray(); + value.ToArray().ForEach(writer.WriteStringValue); + writer.WriteEndArray(); + return; + } + + var text = (string?)value; + if (text is not null) + writer.WriteStringValue(text); + else + writer.WriteNullValue(); + } +} diff --git a/src/TypeCache/Converters/ValueJsonConverter.cs b/src/TypeCache/Converters/ValueJsonConverter.cs index 7c557805..c5123a5d 100644 --- a/src/TypeCache/Converters/ValueJsonConverter.cs +++ b/src/TypeCache/Converters/ValueJsonConverter.cs @@ -28,6 +28,7 @@ private static IDictionary GetObject(ref Utf8JsonReader reader, dictionary.Add(name!, value!); } } + return dictionary; } @@ -39,6 +40,7 @@ private static object[] GetArray(ref Utf8JsonReader reader, JsonSerializerOption var value = JsonSerializer.Deserialize(ref reader, options); list.Add(value!); } + return list.ToArray(); } } diff --git a/src/TypeCache/Extensions/ArrayExtensions.cs b/src/TypeCache/Extensions/ArrayExtensions.cs index 8a3c4c77..218abbcf 100644 --- a/src/TypeCache/Extensions/ArrayExtensions.cs +++ b/src/TypeCache/Extensions/ArrayExtensions.cs @@ -7,6 +7,9 @@ namespace TypeCache.Extensions; +public delegate void ActionRef(ref T item); +public delegate void ActionIndexRef(ref T item, int index); + public static class ArrayExtensions { /// diff --git a/src/TypeCache/Extensions/AssertExtensions.cs b/src/TypeCache/Extensions/AssertExtensions.cs index f78d5dde..50e5abda 100644 --- a/src/TypeCache/Extensions/AssertExtensions.cs +++ b/src/TypeCache/Extensions/AssertExtensions.cs @@ -100,10 +100,7 @@ public static void AssertNotNull([NotNull] this T? @this, [CallerArgumentExpression("this")] string? argument = null, [CallerMemberName] string? caller = null) where T : notnull - { - if (@this is null) - throw new ArgumentNullException(argument, Invariant($"{caller}: {argument}.{nameof(AssertNotNull)}<{typeof(T).Name}>().")); - } + => ArgumentNullException.ThrowIfNull(@this, Invariant($"{caller}: {argument}.{nameof(AssertNotNull)}<{typeof(T).Name}>().")); /// public static void AssertNotSame(this object? @this, object? value, StringComparison comparison = StringComparison.OrdinalIgnoreCase, diff --git a/src/TypeCache/Extensions/EnumExtensions.cs b/src/TypeCache/Extensions/EnumExtensions.cs index dc47cbd8..87b842f1 100644 --- a/src/TypeCache/Extensions/EnumExtensions.cs +++ b/src/TypeCache/Extensions/EnumExtensions.cs @@ -1,6 +1,6 @@ // Copyright (c) 2021 Samuel Abraham -using TypeCache.Collections; +using System.Collections.Immutable; using TypeCache.Extensions; using TypeCache.Utilities; @@ -9,34 +9,46 @@ namespace TypeCache.Extensions; public static class EnumExtensions { [DebuggerHidden] - public static IReadOnlyCollection Attributes(this T @this) + public static IReadOnlyList Attributes(this T @this) where T : struct, Enum - => EnumOf.Tokens.TryGetValue(@this, out var token) ? token.Attributes : Array.Empty; + => Enum.Tokens[@this]?.Attributes ?? ImmutableArray.Empty; [DebuggerHidden] public static bool HasAnyFlag(this T @this, params T[] flags) where T : struct, Enum => flags?.Any(flag => @this.HasFlag(flag)) ?? false; - [DebuggerHidden] + /// + /// => @.ToString("X"); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] public static string Hex(this T @this) where T : struct, Enum - => EnumOf.Tokens.TryGetValue(@this, out var token) ? token.Hex : @this.ToString("X"); + => @this.ToString("X"); - [DebuggerHidden] + /// + /// => .IsDefined(@); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsDefined(this T @this) where T : struct, Enum - => EnumOf.Tokens.ContainsKey(@this); + => Enum.IsDefined(@this); + /// + /// => @.ToString("F"); + /// [DebuggerHidden] public static string Name(this T @this) where T : struct, Enum - => EnumOf.Tokens.TryGetValue(@this, out var token) ? token.Name : @this.ToString(); + => @this.ToString("F"); - [DebuggerHidden] + /// + /// => @.ToString("D"); + /// + [MethodImpl(AggressiveInlining), DebuggerHidden] public static string Number(this T @this) where T : struct, Enum - => EnumOf.Tokens.TryGetValue(@this, out var token) ? token.Number : @this.ToString("D"); + => @this.ToString("D"); /// /// diff --git a/src/TypeCache/Extensions/EnumerableExtensions.cs b/src/TypeCache/Extensions/EnumerableExtensions.cs index 30e74f8a..455a8fae 100644 --- a/src/TypeCache/Extensions/EnumerableExtensions.cs +++ b/src/TypeCache/Extensions/EnumerableExtensions.cs @@ -38,12 +38,12 @@ public static List AsList(this IEnumerable? @this) => @this as List ?? @this?.ToList() ?? new List(0); /// - /// => @.Contains(, ); + /// => @.Contains(, ()); /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool ContainsEnum(this IEnumerable @this, T value) where T : struct, Enum - => @this.Contains(value, EnumOf.Comparer); + => @this.Contains(value, new EnumComparer()); /// /// diff --git a/src/TypeCache/Extensions/ExpressionExtensions.cs b/src/TypeCache/Extensions/ExpressionExtensions.cs index 3ffb42b7..fbae5615 100644 --- a/src/TypeCache/Extensions/ExpressionExtensions.cs +++ b/src/TypeCache/Extensions/ExpressionExtensions.cs @@ -228,7 +228,7 @@ public static Expression Convert(this Expression @this, Type targetType) if (@this.Type != typeof(object)) return ValueConverter.CreateConversionExpression(@this, targetType); - var targetScalarType = targetType.GetDataType(); + var targetScalarType = targetType.GetScalarType(); var expression = targetScalarType switch { ScalarType.BigInteger => (Expression)typeof(ValueConverter).ToStaticMethodCallExpression(nameof(ValueConverter.ConvertToBigInteger), @this), diff --git a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs index ceda3fe0..556a6c09 100644 --- a/src/TypeCache/Extensions/ReflectionExtensions.Type.cs +++ b/src/TypeCache/Extensions/ReflectionExtensions.Type.cs @@ -67,7 +67,11 @@ partial class ReflectionExtensions public static CollectionType GetCollectionType(this Type @this) => TypeStore.CollectionTypes[@this.IsGenericType ? @this.GetGenericTypeDefinition().TypeHandle : @this.TypeHandle]; - public static ScalarType GetDataType(this Type @this) + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static ObjectType GetObjectType(this Type @this) + => TypeStore.ObjectTypes[@this.IsGenericType ? @this.GetGenericTypeDefinition().TypeHandle : @this.TypeHandle]; + + public static ScalarType GetScalarType(this Type @this) { if (@this.IsGenericTypeDefinition) return ScalarType.None; @@ -81,10 +85,6 @@ public static ScalarType GetDataType(this Type @this) return TypeStore.DataTypes.TryGetValue(@this.TypeHandle, out var dataType) ? dataType : ScalarType.None; } - [MethodImpl(AggressiveInlining), DebuggerHidden] - public static ObjectType GetObjectType(this Type @this) - => TypeStore.ObjectTypes[@this.IsGenericType ? @this.GetGenericTypeDefinition().TypeHandle : @this.TypeHandle]; - /// /// /// => @.GetFields( | | ); @@ -359,7 +359,7 @@ public static bool IsConvertibleTo(this Type @this, Type targetType) @this.AssertNotNull(); targetType.AssertNotNull(); - return @this.GetDataType().IsConvertibleTo(targetType.GetDataType()); + return @this.GetScalarType().IsConvertibleTo(targetType.GetScalarType()); } /// @@ -381,7 +381,7 @@ public static bool IsEnumerableOf(this Type @this) /// [MethodImpl(AggressiveInlining), DebuggerHidden] public static bool IsEnumUnderlyingType(this Type @this) - => @this.GetDataType().IsEnumUnderlyingType(); + => @this.GetScalarType().IsEnumUnderlyingType(); /// /// => @.IsPointer && !@.IsByRef && !@.IsByRefLike; diff --git a/src/TypeCache/Extensions/SpanExtensions.cs b/src/TypeCache/Extensions/SpanExtensions.cs index b7ae2235..99e361c1 100644 --- a/src/TypeCache/Extensions/SpanExtensions.cs +++ b/src/TypeCache/Extensions/SpanExtensions.cs @@ -71,7 +71,7 @@ public static bool TryRead(this scoped Span @this, out T value) /// => .TryWrite(@, ); /// [MethodImpl(AggressiveInlining), DebuggerHidden] - public static bool TryWrite(this scoped Span @this, T value) + public static bool TryWrite(this scoped Span @this, ref T value) where T : struct => MemoryMarshal.TryWrite(@this, ref value); diff --git a/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs b/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs index 17538cff..72680931 100644 --- a/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs +++ b/src/TypeCache/Net/Mediation/HttpClientValidationRule.cs @@ -8,10 +8,9 @@ namespace TypeCache.Net.Mediation; internal sealed class HttpClientValidationRule : IValidationRule { - public Task ValidateAsync(HttpClientRequest request, CancellationToken token) - => Task.Run(() => - { - request?.Message?.RequestUri.AssertNotNull(); - request!.Message.RequestUri!.IsAbsoluteUri.AssertTrue(); - }, token); + public Task ValidateAsync(HttpClientRequest request, CancellationToken token) => Task.Run(() => + { + request?.Message?.RequestUri.AssertNotNull(); + request!.Message.RequestUri!.IsAbsoluteUri.AssertTrue(); + }, token); } diff --git a/src/TypeCache/TypeCache.csproj b/src/TypeCache/TypeCache.csproj index 07780874..e1a966de 100644 --- a/src/TypeCache/TypeCache.csproj +++ b/src/TypeCache/TypeCache.csproj @@ -6,7 +6,7 @@ TypeCache true TypeCache - 7.4.0 + 7.4.1 Samuel Abraham <sam987883@gmail.com> Samuel Abraham <sam987883@gmail.com> TypeCache Reflection diff --git a/src/TypeCache/Utilities/Enum.cs b/src/TypeCache/Utilities/Enum.cs new file mode 100644 index 00000000..67fd558f --- /dev/null +++ b/src/TypeCache/Utilities/Enum.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2021 Samuel Abraham + +using System.Collections; +using System.Collections.Immutable; +using TypeCache.Collections; +using TypeCache.Extensions; + +namespace TypeCache.Utilities; + +public static class Enum + where T : struct, Enum +{ + static Enum() + { + var attributes = typeof(T).GetCustomAttributes(false); + Attributes = attributes.Length > 0 ? attributes.Cast().ToImmutableArray() : ImmutableArray.Empty; + Flags = attributes.Any(); + Name = typeof(T).Name; + Tokens = new TokenCollection(); + } + + [DebuggerHidden] + public static IReadOnlyList Attributes { get; } + + [DebuggerHidden] + public static bool Flags { get; } + + [DebuggerHidden] + public static string Name { get; } + + [DebuggerHidden] + public static TokenCollection Tokens { get; } + + [DebuggerHidden] + public static Type UnderlyingType => typeof(T).GetEnumUnderlyingType(); + + public class TokenCollection : IReadOnlyList> + { + private readonly IReadOnlyList> _Tokens; + + internal TokenCollection() + { + this._Tokens = Enum.GetValues().Select(value => new Token(value)).ToImmutableArray(); + } + + public Token this[int index] => this._Tokens[index]; + + public Token? this[T value] + { + get + { + var comparer = new EnumComparer(); + return this._Tokens.FirstOrDefault(token => comparer.EqualTo(token.Value, value)); + } + } + + public Token? this[string name, StringComparison comparison = StringComparison.OrdinalIgnoreCase] + => this._Tokens.FirstOrDefault(token => token.Name.Equals(name, comparison)); + + public int Count => this._Tokens.Count; + + [MethodImpl(AggressiveInlining), DebuggerHidden] + public IEnumerator> GetEnumerator() + => this._Tokens.GetEnumerator(); + + [MethodImpl(AggressiveInlining), DebuggerHidden] + IEnumerator IEnumerable.GetEnumerator() + => ((IEnumerable)this._Tokens).GetEnumerator(); + } +} diff --git a/src/TypeCache/Utilities/EnumOf.cs b/src/TypeCache/Utilities/EnumOf.cs deleted file mode 100644 index 061c12ce..00000000 --- a/src/TypeCache/Utilities/EnumOf.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -using System.Collections.Immutable; -using System.Reflection; -using TypeCache.Collections; -using TypeCache.Extensions; - -namespace TypeCache.Utilities; - -public static class EnumOf - where T : struct, Enum -{ - static EnumOf() - { - var type = typeof(T); - - Attributes = type.GetCustomAttributes().ToImmutableArray(); - Comparer = new EnumComparer(); - Flags = Attributes.Any(); - Name = type.Name; - Tokens = Enum.GetValues().ToImmutableDictionary(value => value, value => new Token(value), Comparer); - } - - public static IReadOnlyCollection Attributes { get; } - - public static EnumComparer Comparer { get; } - - public static bool Flags { get; } - - public static string Name { get; } - - public static IReadOnlyDictionary> Tokens { get; } - - public static Type UnderlyingType => typeof(T).GetEnumUnderlyingType(); -} diff --git a/src/TypeCache/Utilities/EventOf.cs b/src/TypeCache/Utilities/EventOf.cs index 37186915..76253795 100644 --- a/src/TypeCache/Utilities/EventOf.cs +++ b/src/TypeCache/Utilities/EventOf.cs @@ -44,6 +44,7 @@ public static bool RemoveEventHandler(long key) return EventHandlers.Remove(key); } + return false; } } diff --git a/src/TypeCache/Utilities/HashMaker.cs b/src/TypeCache/Utilities/HashMaker.cs index c2cd9ad4..6a209617 100644 --- a/src/TypeCache/Utilities/HashMaker.cs +++ b/src/TypeCache/Utilities/HashMaker.cs @@ -7,30 +7,28 @@ namespace TypeCache.Utilities; public class HashMaker : IHashMaker { - private readonly PaddingMode _PaddingMode; - private readonly Aes _Provider = Aes.Create(); + private readonly Aes _SymmetricAlgorithm = Aes.Create(); /// - public HashMaker(byte[] rgbKey, byte[] rgbIV, PaddingMode paddingMode = PaddingMode.PKCS7) + public HashMaker(byte[] rgbKey, byte[] rgbIV) { - this._Provider.Key = rgbKey; - this._Provider.IV = rgbIV; - this._PaddingMode = paddingMode; + this._SymmetricAlgorithm.Key = rgbKey; + this._SymmetricAlgorithm.IV = rgbIV; } /// - public byte[] Decrypt(ReadOnlySpan data) - => this._Provider - .DecryptCbc(data, this._Provider.IV, this._PaddingMode); + public byte[] Decrypt(ReadOnlySpan data, PaddingMode paddingMode = PaddingMode.PKCS7) + => this._SymmetricAlgorithm + .DecryptCbc(data, this._SymmetricAlgorithm.IV, paddingMode); /// - public byte[] Decrypt(byte[] data) - => this._Provider - .DecryptCbc(data, this._Provider.IV, this._PaddingMode); + public byte[] Decrypt(byte[] data, PaddingMode paddingMode = PaddingMode.PKCS7) + => this._SymmetricAlgorithm + .DecryptCbc(data, this._SymmetricAlgorithm.IV, paddingMode); /// /// A base 64 encoded string. - public long Decrypt(ReadOnlySpan hashId) + public long Decrypt(ReadOnlySpan hashId, PaddingMode paddingMode = PaddingMode.PKCS7) { var length = (hashId[^2], hashId[^1]) switch { @@ -47,31 +45,30 @@ public long Decrypt(ReadOnlySpan hashId) .Replace('_', '/') .ToArray() .FromBase64(); - return this._Provider - .DecryptCbc(data, this._Provider.IV, this._PaddingMode) + return this._SymmetricAlgorithm + .DecryptCbc(data, this._SymmetricAlgorithm.IV, paddingMode) .ToInt64(); } - public byte[] Encrypt(ReadOnlySpan data) - => this._Provider - .EncryptCbc(data, this._Provider.IV, this._PaddingMode); + public byte[] Encrypt(ReadOnlySpan data, PaddingMode paddingMode = PaddingMode.PKCS7) + => this._SymmetricAlgorithm + .EncryptCbc(data, this._SymmetricAlgorithm.IV, paddingMode); /// - public byte[] Encrypt(byte[] data) + public byte[] Encrypt(byte[] data, PaddingMode paddingMode = PaddingMode.PKCS7) { data.AssertNotNull(); - return this._Provider - .EncryptCbc(data, this._Provider.IV, this._PaddingMode); + return this._SymmetricAlgorithm + .EncryptCbc(data, this._SymmetricAlgorithm.IV, paddingMode); } /// /// Returns a base 64 encoded string. - public ReadOnlySpan Encrypt(long id) + public ReadOnlySpan Encrypt(long id, PaddingMode paddingMode = PaddingMode.PKCS7) { - var data = id.GetBytes(); - var chars = this._Provider - .EncryptCbc(data, this._Provider.IV, this._PaddingMode) + var chars = this._SymmetricAlgorithm + .EncryptCbc(id.GetBytes(), this._SymmetricAlgorithm.IV, paddingMode) .ToBase64Chars() .AsSpan() .Replace('+', '-') @@ -81,7 +78,7 @@ public ReadOnlySpan Encrypt(long id) public void Dispose() { - this._Provider.Clear(); - this._Provider.Dispose(); + this._SymmetricAlgorithm.Clear(); + this._SymmetricAlgorithm.Dispose(); } } diff --git a/src/TypeCache/Utilities/IHashMaker.cs b/src/TypeCache/Utilities/IHashMaker.cs index e2f145ad..3a133730 100644 --- a/src/TypeCache/Utilities/IHashMaker.cs +++ b/src/TypeCache/Utilities/IHashMaker.cs @@ -1,18 +1,20 @@ // Copyright (c) 2021 Samuel Abraham +using System.Security.Cryptography; + namespace TypeCache.Utilities; public interface IHashMaker : IDisposable { - byte[] Decrypt(ReadOnlySpan data); + byte[] Decrypt(ReadOnlySpan data, PaddingMode paddingMode = PaddingMode.PKCS7); - byte[] Decrypt(byte[] data); + byte[] Decrypt(byte[] data, PaddingMode paddingMode = PaddingMode.PKCS7); - long Decrypt(ReadOnlySpan hashId); + long Decrypt(ReadOnlySpan hashId, PaddingMode paddingMode = PaddingMode.PKCS7); - byte[] Encrypt(ReadOnlySpan data); + byte[] Encrypt(ReadOnlySpan data, PaddingMode paddingMode = PaddingMode.PKCS7); - byte[] Encrypt(byte[] data); + byte[] Encrypt(byte[] data, PaddingMode paddingMode = PaddingMode.PKCS7); - ReadOnlySpan Encrypt(long id); + ReadOnlySpan Encrypt(long id, PaddingMode paddingMode = PaddingMode.PKCS7); } diff --git a/src/TypeCache/Utilities/LambdaFactory.cs b/src/TypeCache/Utilities/LambdaFactory.cs index c4adee99..cd8a3635 100644 --- a/src/TypeCache/Utilities/LambdaFactory.cs +++ b/src/TypeCache/Utilities/LambdaFactory.cs @@ -8,178 +8,152 @@ namespace TypeCache.Utilities; public static class LambdaFactory { - public static LambdaExpression Create(Type[] parameterTypes, Func bodyFactory) + public static LambdaExpression Create(Type[] parameterTypes, Func lambda) { var parameters = parameterTypes.Select((type, i) => Invariant($"parameter{i + 1}").ToParameterExpression(type)).ToArray(); - return bodyFactory(parameters).Lambda(parameters); + return lambda(parameters).Lambda(parameters); } public static Expression> CreateAction( - Func bodyFactory) + Func lambda) { ParameterExpression parameter = nameof(parameter).ToParameterExpression(); - return Expression.Lambda>(bodyFactory(parameter), parameter); + return Expression.Lambda>(lambda(parameter), parameter); } - public static LambdaExpression CreateAction(Type[] parameterTypes, Func bodyFactory) + public static LambdaExpression CreateAction(Type[] parameterTypes, Func lambda) { var parameters = parameterTypes.Select((type, i) => Invariant($"parameter{i + 1}").ToParameterExpression(type)).ToArray(); - return bodyFactory(parameters).LambdaAction(parameters); + return lambda(parameters).LambdaAction(parameters); } public static Expression> CreateAction( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2), parameter1, parameter2); } public static Expression> CreateAction( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3), parameter1, parameter2, parameter3); } public static Expression> CreateAction( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression(), - "parameter4".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2], parameters[3]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + ParameterExpression parameter4 = nameof(parameter4).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3, parameter4), parameter1, parameter2, parameter3, parameter4); } public static Expression> CreateAction( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression(), - "parameter4".ToParameterExpression(), - "parameter5".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + ParameterExpression parameter4 = nameof(parameter4).ToParameterExpression(); + ParameterExpression parameter5 = nameof(parameter5).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3, parameter4, parameter5), parameter1, parameter2, parameter3, parameter4, parameter5); } public static Expression> CreateAction( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression(), - "parameter4".ToParameterExpression(), - "parameter5".ToParameterExpression(), - "parameter6".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4], parameters[5]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + ParameterExpression parameter4 = nameof(parameter4).ToParameterExpression(); + ParameterExpression parameter5 = nameof(parameter5).ToParameterExpression(); + ParameterExpression parameter6 = nameof(parameter6).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3, parameter4, parameter5, parameter6), parameter1, parameter2, parameter3, parameter4, parameter5, parameter6); } public static Expression> CreateComparison( - Func bodyFactory) + Func lambda) { ParameterExpression value1 = nameof(value1).ToParameterExpression(); ParameterExpression value2 = nameof(value2).ToParameterExpression(); - return Expression.Lambda>(bodyFactory(value1, value2), value1, value2); + return Expression.Lambda>(lambda(value1, value2), value1, value2); } - public static LambdaExpression CreateFunc(Type[] parameterTypes, Type returnType, Func bodyFactory) + public static LambdaExpression CreateFunc(Type[] parameterTypes, Type returnType, Func lambda) { var parameters = parameterTypes.Select((type, i) => Invariant($"parameter{i + 1}").ToParameterExpression(type)).ToArray(); - return bodyFactory(parameters).LambdaFunc(returnType, parameters); + return lambda(parameters).LambdaFunc(returnType, parameters); } - public static Expression> CreateFunc(Func bodyFactory) + [MethodImpl(AggressiveInlining), DebuggerHidden] + public static LambdaExpression CreateFunc(Expression> expression) + => expression; + + public static Expression> CreateFunc(Func lambda) { ParameterExpression parameter = nameof(parameter).ToParameterExpression(); - return Expression.Lambda>(bodyFactory(parameter), parameter); + return Expression.Lambda>(lambda(parameter), parameter); } public static Expression> CreateFunc( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2), parameter1, parameter2); } public static Expression> CreateFunc( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3), parameter1, parameter2, parameter3); } public static Expression> CreateFunc( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression(), - "parameter4".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2], parameters[3]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + ParameterExpression parameter4 = nameof(parameter4).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3, parameter4), parameter1, parameter2, parameter3, parameter4); } public static Expression> CreateFunc( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression(), - "parameter4".ToParameterExpression(), - "parameter5".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + ParameterExpression parameter4 = nameof(parameter4).ToParameterExpression(); + ParameterExpression parameter5 = nameof(parameter5).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3, parameter4, parameter5), parameter1, parameter2, parameter3, parameter4, parameter5); } public static Expression> CreateFunc( - Func bodyFactory) + Func lambda) { - var parameters = new[] - { - "parameter1".ToParameterExpression(), - "parameter2".ToParameterExpression(), - "parameter3".ToParameterExpression(), - "parameter4".ToParameterExpression(), - "parameter5".ToParameterExpression(), - "parameter6".ToParameterExpression() - }; - return Expression.Lambda>(bodyFactory(parameters[0], parameters[1], parameters[2], parameters[3], parameters[4], parameters[5]), parameters); + ParameterExpression parameter1 = nameof(parameter1).ToParameterExpression(); + ParameterExpression parameter2 = nameof(parameter2).ToParameterExpression(); + ParameterExpression parameter3 = nameof(parameter3).ToParameterExpression(); + ParameterExpression parameter4 = nameof(parameter4).ToParameterExpression(); + ParameterExpression parameter5 = nameof(parameter5).ToParameterExpression(); + ParameterExpression parameter6 = nameof(parameter6).ToParameterExpression(); + return Expression.Lambda>(lambda(parameter1, parameter2, parameter3, parameter4, parameter5, parameter6), parameter1, parameter2, parameter3, parameter4, parameter5, parameter6); } - public static Expression> CreatePredicate(Func bodyFactory) + public static Expression> CreatePredicate(Func lambda) { ParameterExpression value = nameof(value).ToParameterExpression(); - return Expression.Lambda>(bodyFactory(value), value); + return Expression.Lambda>(lambda(value), value); } } diff --git a/src/TypeCache/Utilities/RegexCache.cs b/src/TypeCache/Utilities/RegexCache.cs index 89f7aade..cd0cb497 100644 --- a/src/TypeCache/Utilities/RegexCache.cs +++ b/src/TypeCache/Utilities/RegexCache.cs @@ -8,19 +8,12 @@ namespace TypeCache.Utilities; public static class RegexCache { - private static readonly TimeSpan RegexTimeout = TimeSpan.FromMinutes(1); + private static readonly TimeSpan DefaultMatchTimeout = TimeSpan.FromMinutes(1); - private static readonly IReadOnlyDictionary MultilineRegex = new LazyDictionary(pattern => - new Regex(pattern, Compiled | CultureInvariant | Multiline, RegexTimeout), comparer: StringComparer.Ordinal); - - private static readonly IReadOnlyDictionary SinglelineRegex = new LazyDictionary(pattern => - new Regex(pattern, Compiled | CultureInvariant | Singleline, RegexTimeout), comparer: StringComparer.Ordinal); - - [MethodImpl(AggressiveInlining), DebuggerHidden] - public static Regex MultilinePattern([StringSyntax(StringSyntaxAttribute.Regex)] this string @this) - => MultilineRegex[@this]; + private static readonly IReadOnlyDictionary<(string Pattern, RegexOptions Options), Regex> Cache = + new LazyDictionary<(string Pattern, RegexOptions Options), Regex>(_ => new(_.Pattern, _.Options, DefaultMatchTimeout)); [MethodImpl(AggressiveInlining), DebuggerHidden] - public static Regex SinglelinePattern([StringSyntax(StringSyntaxAttribute.Regex)] this string @this) - => SinglelineRegex[@this]; + public static Regex Regex([StringSyntax(StringSyntaxAttribute.Regex)] this string @this, RegexOptions options = Compiled | CultureInvariant | Singleline) + => Cache[(@this, options)]; } diff --git a/src/TypeCache/Utilities/Token.cs b/src/TypeCache/Utilities/Token.cs index 5ff7f25a..18bf1ccd 100644 --- a/src/TypeCache/Utilities/Token.cs +++ b/src/TypeCache/Utilities/Token.cs @@ -1,44 +1,27 @@ // Copyright (c) 2021 Samuel Abraham using System.Collections.Immutable; -using System.Reflection; using TypeCache.Extensions; using static System.Reflection.BindingFlags; namespace TypeCache.Utilities; [DebuggerDisplay("{Token}.{Name,nq}", Name = "{Name}")] -public sealed class Token : IEquatable> +public sealed class Token where T : struct, Enum { internal Token(T value) { - var fieldInfo = typeof(T).GetField(value.ToString(), Public | Static)!; - this.Attributes = fieldInfo.GetCustomAttributes().ToImmutableArray(); - this.Name = fieldInfo.Name(); + var fieldInfo = typeof(T).GetField(Enum.GetName(value)!, Public | Static)!; + var attributes = fieldInfo.GetCustomAttributes(false); + this.Attributes = attributes.Length > 0 ? attributes.Cast().ToImmutableArray() : ImmutableArray.Empty; + this.Name = fieldInfo.Name; this.Value = value; - this.Hex = value.ToString("X"); - this.Number = value.ToString("D"); } - public IReadOnlyCollection Attributes { get; } + public IReadOnlyList Attributes { get; } public string Name { get; } - public string Hex { get; } - - public string Number { get; } - public T Value { get; } - - public bool Equals([NotNullWhen(true)] Token? other) - => other is not null && EnumOf.Comparer.Equals(this.Value, other.Value); - - [MethodImpl(AggressiveInlining), DebuggerHidden] - public override bool Equals([NotNullWhen(true)] object? item) - => this.Equals(item as Token); - - [MethodImpl(AggressiveInlining), DebuggerHidden] - public override int GetHashCode() - => this.Value.GetHashCode(); } diff --git a/src/TypeCache/Utilities/TypeStore.cs b/src/TypeCache/Utilities/TypeStore.cs index 18c73d5d..43ccbbef 100644 --- a/src/TypeCache/Utilities/TypeStore.cs +++ b/src/TypeCache/Utilities/TypeStore.cs @@ -144,6 +144,8 @@ static TypeStore() return CollectionType.None; }); + DefaultValueFactory = new LazyDictionary>(handle => + handle.ToType().ToDefaultExpression().As().Lambda>().Compile()); DefaultValueTypeConstructorInvokes = new LazyDictionary>(handle => handle.ToType().ToNewExpression().As().Lambda>().Compile()); FieldGetInvokes = new(); @@ -165,7 +167,7 @@ static TypeStore() if (type == typeof(object)) return ObjectType.Object; - if (type.IsPrimitive || type.GetDataType() is not ScalarType.None) + if (type.IsPrimitive || type.GetScalarType() is not ScalarType.None) return ObjectType.DataType; var count = ObjectTypeMap.Count; @@ -213,6 +215,8 @@ static TypeStore() }.ToImmutableDictionary(); } + public static IReadOnlyDictionary> DefaultValueFactory { get; } + public static IReadOnlyDictionary> DefaultValueTypeConstructorInvokes { get; } public static ConcurrentDictionary> FieldGetInvokes { get; } diff --git a/src/TypeCache/Utilities/ValueConverter.cs b/src/TypeCache/Utilities/ValueConverter.cs index 76813cb6..9a140596 100644 --- a/src/TypeCache/Utilities/ValueConverter.cs +++ b/src/TypeCache/Utilities/ValueConverter.cs @@ -8,9 +8,8 @@ namespace TypeCache.Utilities; public static class ValueConverter { + private const string TRUE_CHARS = "1XxYyTt"; - /// - /// public static Expression CreateConversionExpression(this Expression @this, Type targetType) { @this.AssertNotNull(); @@ -18,107 +17,158 @@ public static Expression CreateConversionExpression(this Expression @this, Type var isSourceNullable = @this.Type.IsNullable(); var value = isSourceNullable ? @this.Property(nameof(Nullable.Value)) : @this; - var expression = (@this.Type.GetDataType(), targetType.GetDataType()) switch + var sourceScalarType = @this.Type.GetScalarType(); + var targetScalarType = targetType.GetScalarType(); + + if (targetScalarType == ScalarType.DBNull) + return DBNull.Value.ToConstantExpression(); + + if (sourceScalarType == ScalarType.DBNull) + return Expression.Constant(null); + + var expression = (sourceScalarType, targetScalarType) switch { - (ScalarType.DBNull, _) when targetType.IsNullable() => Expression.Constant(null), - (_, ScalarType.DBNull) when isSourceNullable => DBNull.Value.ToConstantExpression(), - - (ScalarType.Boolean, ScalarType.Char) => value.IIf('1'.ToConstantExpression(), '0'.ToConstantExpression()), - (ScalarType.Boolean, ScalarType.SByte) => value.IIf(((sbyte)1).ToConstantExpression(), ((sbyte)0).ToConstantExpression()), - (ScalarType.Boolean, ScalarType.Int16) => value.IIf(((short)1).ToConstantExpression(), ((short)0).ToConstantExpression()), - (ScalarType.Boolean, ScalarType.Int32) => value.IIf(1.ToConstantExpression(), 0.ToConstantExpression()), - (ScalarType.Boolean, ScalarType.Int64) => value.IIf(1L.ToConstantExpression(), 0L.ToConstantExpression()), - (ScalarType.Boolean, ScalarType.Byte) => value.IIf(((byte)1).ToConstantExpression(), ((byte)0).ToConstantExpression()), - (ScalarType.Boolean, ScalarType.UInt16) => value.IIf(((ushort)1).ToConstantExpression(), ((ushort)0).ToConstantExpression()), - (ScalarType.Boolean, ScalarType.UInt32) => value.IIf(1U.ToConstantExpression(), 0U.ToConstantExpression()), - (ScalarType.Boolean, ScalarType.UInt64) => value.IIf(1UL.ToConstantExpression(), 0UL.ToConstantExpression()), - - (ScalarType.Char, ScalarType.Boolean) => ((LambdaExpression)((char value) => - value == '1' || value == 'x' || value == 'X' || value == 'y' || value == 'Y' || value == 't' || value == 'T')).Invoke(value), - (ScalarType.SByte, ScalarType.Boolean) => value.Operation(BinaryOperator.NotEqualTo, ((sbyte)0).ToConstantExpression()), - (ScalarType.Int16, ScalarType.Boolean) => value.Operation(BinaryOperator.NotEqualTo, ((short)0).ToConstantExpression()), - (ScalarType.Int32, ScalarType.Boolean) => value.Operation(BinaryOperator.NotEqualTo, 0.ToConstantExpression()), - (ScalarType.Int64, ScalarType.Boolean) => value.Operation(BinaryOperator.NotEqualTo, 0L.ToConstantExpression()), - (ScalarType.Int128, ScalarType.Boolean) => value.Operation(BinaryOperator.NotEqualTo, Int128.Zero.ToConstantExpression()), - (ScalarType.BigInteger, ScalarType.Boolean) => value.Operation(BinaryOperator.NotEqualTo, BigInteger.Zero.ToConstantExpression()), - (ScalarType.Byte, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, ((byte)0).ToConstantExpression()), - (ScalarType.UInt16, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, ((ushort)0).ToConstantExpression()), - (ScalarType.UInt32, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, 0U.ToConstantExpression()), - (ScalarType.UInt64, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, 0UL.ToConstantExpression()), - (ScalarType.UInt128, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, UInt128.Zero.ToConstantExpression()), - (ScalarType.IntPtr, ScalarType.Boolean) => value.Operation(BinaryOperator.NotEqualTo, nint.Zero.ToConstantExpression()), - (ScalarType.UIntPtr, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, nuint.Zero.ToConstantExpression()), - (ScalarType.Half, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, ((Half)0).ToConstantExpression()), - (ScalarType.Single, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, 0F.ToConstantExpression()), - (ScalarType.Double, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, 0D.ToConstantExpression()), - (ScalarType.Decimal, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, 0M.ToConstantExpression()), - (ScalarType.DateOnly, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, DateOnly.MinValue.ToConstantExpression()), - (ScalarType.DateTime, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, DateTime.MinValue.ToConstantExpression()), - (ScalarType.DateTimeOffset, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, DateTimeOffset.MinValue.ToConstantExpression()), - (ScalarType.TimeOnly, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, TimeOnly.MinValue.ToConstantExpression()), - (ScalarType.TimeSpan, ScalarType.Boolean) => value.Operation(BinaryOperator.GreaterThan, TimeSpan.Zero.ToConstantExpression()), - - (ScalarType.DateOnly, ScalarType.String) => ((LambdaExpression)((DateOnly value) => value.ToISO8601(null))).Invoke(value).ReduceExtensions(), - (ScalarType.DateTime, ScalarType.String) => ((LambdaExpression)((DateTime value) => value.ToISO8601(null))).Invoke(value).ReduceExtensions(), - (ScalarType.DateTimeOffset, ScalarType.String) => ((LambdaExpression)((DateTimeOffset value) => value.ToISO8601(null))).Invoke(value).ReduceExtensions(), - (ScalarType.Enum, ScalarType.String) => value.Call(nameof(Enum.ToString), "F".ToConstantExpression()), - (ScalarType.TimeOnly, ScalarType.String) => ((LambdaExpression)((TimeOnly value) => value.ToISO8601(null))).Invoke(value).ReduceExtensions(), - (ScalarType.TimeSpan, ScalarType.String) => ((LambdaExpression)((TimeSpan value) => value.ToText(null))).Invoke(value).ReduceExtensions(), - (_, ScalarType.String) => value.Call(nameof(object.ToString)), - - (ScalarType.Int32, ScalarType.Index) => typeof(Index).ToNewExpression(value), - (ScalarType.UInt32, ScalarType.Index) => typeof(Index).ToNewExpression(value.Cast(true)), - - (ScalarType.Int32, ScalarType.DateOnly) => typeof(DateOnly).ToStaticMethodCallExpression(nameof(DateOnly.FromDayNumber), value), - (ScalarType.UInt32, ScalarType.DateOnly) => typeof(DateOnly).ToStaticMethodCallExpression(nameof(DateOnly.FromDayNumber), value.Cast(true)), - - (ScalarType.Int64, ScalarType.DateTime) => typeof(DateTime).ToNewExpression(value), - (ScalarType.UInt64, ScalarType.DateTime) => typeof(DateTime).ToNewExpression(value.Cast(true)), - - (ScalarType.DateOnly, ScalarType.DateTime) => ((LambdaExpression)((DateOnly value) => value.ToDateTime(TimeOnly.MinValue))).Invoke(value), - (ScalarType.DateOnly, ScalarType.DateTimeOffset) => ((LambdaExpression)((DateOnly value) => value.ToDateTime(TimeOnly.MinValue).ToDateTimeOffset())).Invoke(value), - (ScalarType.DateOnly, ScalarType.TimeSpan) => ((LambdaExpression)((DateOnly value) => TimeSpan.FromDays(value.DayNumber))).Invoke(value), - (ScalarType.DateOnly, ScalarType.Int32) => value.Property(nameof(DateOnly.DayNumber)), - (ScalarType.DateOnly, ScalarType.UInt32) => value.Property(nameof(DateOnly.DayNumber)).Cast(), - (ScalarType.DateOnly, ScalarType.Int64) => value.Property(nameof(DateOnly.DayNumber)).Cast(), - (ScalarType.DateOnly, ScalarType.UInt64) => value.Property(nameof(DateOnly.DayNumber)).Cast(), - - (ScalarType.DateTime, ScalarType.DateOnly) => ((LambdaExpression)((DateTime value) => value.ToDateOnly())).Invoke(value).ReduceExtensions(), - (ScalarType.DateTime, ScalarType.DateTimeOffset) => ((LambdaExpression)((DateTime value) => value.ToDateTimeOffset())).Invoke(value).ReduceExtensions(), - (ScalarType.DateTime, ScalarType.TimeOnly) => ((LambdaExpression)((DateTime value) => value.ToTimeOnly())).Invoke(value).ReduceExtensions(), - (ScalarType.DateTime, ScalarType.Int64) => value.Property(nameof(DateTime.Ticks)), - (ScalarType.DateTime, ScalarType.UInt64) => value.Property(nameof(DateTime.Ticks)).Cast(), - - (ScalarType.DateTimeOffset, ScalarType.DateOnly) => ((LambdaExpression)((DateTimeOffset value) => value.ToDateOnly())).Invoke(value).ReduceExtensions(), - (ScalarType.DateTimeOffset, ScalarType.DateTime) => value.Property(nameof(DateTimeOffset.LocalDateTime)), - (ScalarType.DateTimeOffset, ScalarType.TimeOnly) => ((LambdaExpression)((DateTimeOffset value) => value.ToTimeOnly())).Invoke(value).ReduceExtensions(), - (ScalarType.DateTimeOffset, ScalarType.Int64) => value.Property(nameof(DateTimeOffset.Ticks)), - (ScalarType.DateTimeOffset, ScalarType.UInt64) => value.Property(nameof(DateTimeOffset.Ticks)).Cast(), - - (ScalarType.TimeOnly, ScalarType.TimeSpan) => ((LambdaExpression)((TimeOnly value) => value.ToTimeSpan())).Invoke(value), - (ScalarType.TimeOnly, ScalarType.Int64) => value.Property(nameof(TimeOnly.Ticks)), - (ScalarType.TimeOnly, ScalarType.UInt64) => value.Property(nameof(TimeOnly.Ticks)).Cast(), - - (ScalarType.TimeSpan, ScalarType.TimeOnly) => typeof(TimeOnly).ToStaticMethodCallExpression(nameof(TimeOnly.FromTimeSpan), value), - (ScalarType.TimeSpan, ScalarType.Int64) => value.Property(nameof(TimeSpan.Ticks)), - (ScalarType.TimeSpan, ScalarType.UInt64) => value.Property(nameof(TimeSpan.Ticks)).Cast(), - - (ScalarType.String, ScalarType.Char) => typeof(Convert).ToStaticMethodCallExpression(nameof(System.Convert.ToChar), value), - (ScalarType.String, ScalarType.Enum) => typeof(Enum).ToStaticMethodCallExpression(nameof(Enum.Parse), new[] { targetType }, value, true.ToConstantExpression()), - (ScalarType.String, ScalarType.Uri) => typeof(Uri).ToNewExpression(value), - (ScalarType.String, ScalarType.Boolean - or ScalarType.SByte or ScalarType.Int16 or ScalarType.Int32 or ScalarType.Int64 or ScalarType.Int128 or ScalarType.BigInteger - or ScalarType.Byte or ScalarType.UInt16 or ScalarType.UInt32 or ScalarType.UInt32 or ScalarType.UInt64 or ScalarType.UInt128 - or ScalarType.IntPtr or ScalarType.UIntPtr - or ScalarType.Half or ScalarType.Single or ScalarType.Double or ScalarType.Decimal - or ScalarType.DateOnly or ScalarType.DateTime or ScalarType.DateTimeOffset or ScalarType.TimeOnly or ScalarType.TimeSpan - or ScalarType.Guid) => targetType.ToStaticMethodCallExpression(nameof(int.Parse), value), - - _ => value.Cast(targetType) + (ScalarType.Boolean, ScalarType.Char) => LambdaFactory.CreateFunc((bool _) => _ ? '1' : '0'), + (ScalarType.Boolean, ScalarType.SByte) => LambdaFactory.CreateFunc((bool _) => _ ? (sbyte)1 : (sbyte)0), + (ScalarType.Boolean, ScalarType.Int16) => LambdaFactory.CreateFunc((bool _) => _ ? (short)1 : (short)0), + (ScalarType.Boolean, ScalarType.Int32) => LambdaFactory.CreateFunc((bool _) => _ ? 1 : 0), + (ScalarType.Boolean, ScalarType.Int64) => LambdaFactory.CreateFunc((bool _) => _ ? 1L : 0L), + (ScalarType.Boolean, ScalarType.Int128) => LambdaFactory.CreateFunc((bool _) => _ ? Int128.One : Int128.Zero), + (ScalarType.Boolean, ScalarType.BigInteger) => LambdaFactory.CreateFunc((bool _) => _ ? BigInteger.One : BigInteger.Zero), + (ScalarType.Boolean, ScalarType.Byte) => LambdaFactory.CreateFunc((bool _) => _ ? (byte)1 : (byte)0), + (ScalarType.Boolean, ScalarType.UInt16) => LambdaFactory.CreateFunc((bool _) => _ ? (ushort)1 : (ushort)0), + (ScalarType.Boolean, ScalarType.UInt32) => LambdaFactory.CreateFunc((bool _) => _ ? 1U : 0U), + (ScalarType.Boolean, ScalarType.UInt64) => LambdaFactory.CreateFunc((bool _) => _ ? 1UL : 0UL), + (ScalarType.Boolean, ScalarType.UInt128) => LambdaFactory.CreateFunc((bool _) => _ ? UInt128.One : UInt128.Zero), + + (ScalarType.Char, ScalarType.Boolean) => LambdaFactory.CreateFunc((char _) => TRUE_CHARS.Contains(_)), + (ScalarType.SByte, ScalarType.Boolean) => LambdaFactory.CreateFunc((sbyte _) => _ != 0), + (ScalarType.Int16, ScalarType.Boolean) => LambdaFactory.CreateFunc((short _) => _ != 0), + (ScalarType.Int32, ScalarType.Boolean) => LambdaFactory.CreateFunc((int _) => _ != 0), + (ScalarType.Int64, ScalarType.Boolean) => LambdaFactory.CreateFunc((long _) => _ != 0L), + (ScalarType.Int128, ScalarType.Boolean) => LambdaFactory.CreateFunc((Int128 _) => _ != Int128.Zero), + (ScalarType.BigInteger, ScalarType.Boolean) => LambdaFactory.CreateFunc((BigInteger _) => _ != BigInteger.Zero), + (ScalarType.Byte, ScalarType.Boolean) => LambdaFactory.CreateFunc((byte _) => _ != 0), + (ScalarType.UInt16, ScalarType.Boolean) => LambdaFactory.CreateFunc((ushort _) => _ != 0), + (ScalarType.UInt32, ScalarType.Boolean) => LambdaFactory.CreateFunc((uint _) => _ != 0U), + (ScalarType.UInt64, ScalarType.Boolean) => LambdaFactory.CreateFunc((ulong _) => _ != 0UL), + (ScalarType.UInt128, ScalarType.Boolean) => LambdaFactory.CreateFunc((UInt128 _) => _ != UInt128.Zero), + (ScalarType.IntPtr, ScalarType.Boolean) => LambdaFactory.CreateFunc((IntPtr _) => _ != nint.Zero), + (ScalarType.UIntPtr, ScalarType.Boolean) => LambdaFactory.CreateFunc((UIntPtr _) => _ != nuint.Zero), + (ScalarType.Half, ScalarType.Boolean) => LambdaFactory.CreateFunc((Half _) => _ != Half.Zero), + (ScalarType.Single, ScalarType.Boolean) => LambdaFactory.CreateFunc((float _) => _ != 0F), + (ScalarType.Double, ScalarType.Boolean) => LambdaFactory.CreateFunc((double _) => _ != 0D), + (ScalarType.Decimal, ScalarType.Boolean) => LambdaFactory.CreateFunc((decimal _) => _ != 0M), + (ScalarType.DateOnly, ScalarType.Boolean) => LambdaFactory.CreateFunc((DateOnly _) => _ != DateOnly.MinValue), + (ScalarType.DateTime, ScalarType.Boolean) => LambdaFactory.CreateFunc((DateTime _) => _ != DateTime.MinValue), + (ScalarType.DateTimeOffset, ScalarType.Boolean) => LambdaFactory.CreateFunc((DateTimeOffset _) => _ != DateTimeOffset.MinValue), + (ScalarType.TimeOnly, ScalarType.Boolean) => LambdaFactory.CreateFunc((TimeOnly _) => _ != TimeOnly.MinValue), + (ScalarType.TimeSpan, ScalarType.Boolean) => LambdaFactory.CreateFunc((TimeSpan _) => _ != TimeSpan.Zero), + + (ScalarType.Char, ScalarType.String) => LambdaFactory.CreateFunc((char _) => _.ToString()), + (ScalarType.SByte, ScalarType.String) => LambdaFactory.CreateFunc((sbyte _) => _.ToString()), + (ScalarType.Int16, ScalarType.String) => LambdaFactory.CreateFunc((short _) => _.ToString()), + (ScalarType.Int32, ScalarType.String) => LambdaFactory.CreateFunc((int _) => _.ToString()), + (ScalarType.Int64, ScalarType.String) => LambdaFactory.CreateFunc((long _) => _.ToString()), + (ScalarType.Int128, ScalarType.String) => LambdaFactory.CreateFunc((Int128 _) => _.ToString()), + (ScalarType.BigInteger, ScalarType.String) => LambdaFactory.CreateFunc((BigInteger _) => _.ToString()), + (ScalarType.Byte, ScalarType.String) => LambdaFactory.CreateFunc((sbyte _) => _.ToString()), + (ScalarType.UInt16, ScalarType.String) => LambdaFactory.CreateFunc((short _) => _.ToString()), + (ScalarType.UInt32, ScalarType.String) => LambdaFactory.CreateFunc((int _) => _.ToString()), + (ScalarType.UInt64, ScalarType.String) => LambdaFactory.CreateFunc((long _) => _.ToString()), + (ScalarType.UInt128, ScalarType.String) => LambdaFactory.CreateFunc((Int128 _) => _.ToString()), + (ScalarType.IntPtr, ScalarType.String) => LambdaFactory.CreateFunc((IntPtr _) => _.ToString()), + (ScalarType.UIntPtr, ScalarType.String) => LambdaFactory.CreateFunc((UIntPtr _) => _.ToString()), + (ScalarType.Half, ScalarType.String) => LambdaFactory.CreateFunc((Half _) => _.ToString()), + (ScalarType.Single, ScalarType.String) => LambdaFactory.CreateFunc((float _) => _.ToString()), + (ScalarType.Double, ScalarType.String) => LambdaFactory.CreateFunc((double _) => _.ToString()), + (ScalarType.Decimal, ScalarType.String) => LambdaFactory.CreateFunc((decimal _) => _.ToString()), + (ScalarType.DateOnly, ScalarType.String) => LambdaFactory.CreateFunc((DateOnly _) => _.ToISO8601(null)), + (ScalarType.DateTime, ScalarType.String) => LambdaFactory.CreateFunc((DateTime _) => _.ToISO8601(null)), + (ScalarType.DateTimeOffset, ScalarType.String) => LambdaFactory.CreateFunc((DateTimeOffset _) => _.ToISO8601(null)), + (ScalarType.Enum, ScalarType.String) => LambdaFactory.CreateFunc((Enum _) => _.ToString("F")), + (ScalarType.TimeOnly, ScalarType.String) => LambdaFactory.CreateFunc((TimeOnly _) => _.ToISO8601(null)), + (ScalarType.TimeSpan, ScalarType.String) => LambdaFactory.CreateFunc((TimeSpan _) => _.ToText(null)), + (ScalarType.Guid, ScalarType.String) => LambdaFactory.CreateFunc((Guid _) => _.ToString("D")), + (ScalarType.Index, ScalarType.String) => LambdaFactory.CreateFunc((Index _) => _.Value.ToString()), + (ScalarType.Uri, ScalarType.String) => LambdaFactory.CreateFunc((Uri _) => _.ToString()), + + (ScalarType.Int32, ScalarType.Index) => LambdaFactory.CreateFunc((int _) => new Index(_, false)), + (ScalarType.UInt32, ScalarType.Index) => LambdaFactory.CreateFunc((uint _) => new Index((int)_, false)), + + (ScalarType.Int32, ScalarType.DateOnly) => LambdaFactory.CreateFunc((int _) => DateOnly.FromDayNumber(_)), + (ScalarType.UInt32, ScalarType.DateOnly) => LambdaFactory.CreateFunc((uint _) => DateOnly.FromDayNumber((int)_)), + (ScalarType.Int64, ScalarType.DateOnly) => LambdaFactory.CreateFunc((long _) => DateOnly.FromDayNumber((int)_)), + (ScalarType.UInt64, ScalarType.DateOnly) => LambdaFactory.CreateFunc((ulong _) => DateOnly.FromDayNumber((int)_)), + + (ScalarType.Int64, ScalarType.DateTime) => LambdaFactory.CreateFunc((long _) => new DateTime(_)), + (ScalarType.UInt64, ScalarType.DateTime) => LambdaFactory.CreateFunc((ulong _) => new DateTime((long)_)), + + (ScalarType.DateOnly, ScalarType.DateTime) => LambdaFactory.CreateFunc((DateOnly _) => _.ToDateTime(TimeOnly.MinValue)), + (ScalarType.DateOnly, ScalarType.DateTimeOffset) => LambdaFactory.CreateFunc((DateOnly _) => _.ToDateTime(TimeOnly.MinValue).ToDateTimeOffset()), + (ScalarType.DateOnly, ScalarType.TimeSpan) => LambdaFactory.CreateFunc((DateOnly _) => TimeSpan.FromDays(_.DayNumber)), + (ScalarType.DateOnly, ScalarType.Int32) => LambdaFactory.CreateFunc((DateOnly _) => _.DayNumber), + (ScalarType.DateOnly, ScalarType.UInt32) => LambdaFactory.CreateFunc((DateOnly _) => (uint)_.DayNumber), + (ScalarType.DateOnly, ScalarType.Int64) => LambdaFactory.CreateFunc((DateOnly _) => (long)_.DayNumber), + (ScalarType.DateOnly, ScalarType.UInt64) => LambdaFactory.CreateFunc((DateOnly _) => (ulong)_.DayNumber), + + (ScalarType.DateTime, ScalarType.DateOnly) => LambdaFactory.CreateFunc((DateTime _) => _.ToDateOnly()), + (ScalarType.DateTime, ScalarType.DateTimeOffset) => LambdaFactory.CreateFunc((DateTime _) => _.ToDateTimeOffset()), + (ScalarType.DateTime, ScalarType.TimeOnly) => LambdaFactory.CreateFunc((DateTime _) => _.ToTimeOnly()), + (ScalarType.DateTime, ScalarType.Int64) => LambdaFactory.CreateFunc((DateTime _) => _.Ticks), + (ScalarType.DateTime, ScalarType.UInt64) => LambdaFactory.CreateFunc((DateTime _) => (ulong)_.Ticks), + + (ScalarType.DateTimeOffset, ScalarType.DateOnly) => LambdaFactory.CreateFunc((DateTimeOffset _) => _.ToDateOnly()), + (ScalarType.DateTimeOffset, ScalarType.DateTime) => LambdaFactory.CreateFunc((DateTimeOffset _) => _.LocalDateTime), + (ScalarType.DateTimeOffset, ScalarType.TimeOnly) => LambdaFactory.CreateFunc((DateTimeOffset _) => _.ToTimeOnly()), + (ScalarType.DateTimeOffset, ScalarType.Int64) => LambdaFactory.CreateFunc((DateTimeOffset _) => _.Ticks), + (ScalarType.DateTimeOffset, ScalarType.UInt64) => LambdaFactory.CreateFunc((DateTimeOffset _) => (ulong)_.Ticks), + + (ScalarType.TimeOnly, ScalarType.TimeSpan) => LambdaFactory.CreateFunc((TimeOnly _) => _.ToTimeSpan()), + (ScalarType.TimeOnly, ScalarType.Int64) => LambdaFactory.CreateFunc((TimeOnly _) => _.Ticks), + (ScalarType.TimeOnly, ScalarType.UInt64) => LambdaFactory.CreateFunc((TimeOnly _) => (ulong)_.Ticks), + + (ScalarType.TimeSpan, ScalarType.TimeOnly) => LambdaFactory.CreateFunc((TimeSpan _) => TimeOnly.FromTimeSpan(_)), + (ScalarType.TimeSpan, ScalarType.Int64) => LambdaFactory.CreateFunc((TimeSpan _) => _.Ticks), + (ScalarType.TimeSpan, ScalarType.UInt64) => LambdaFactory.CreateFunc((TimeSpan _) => (ulong)_.Ticks), + + (ScalarType.String, ScalarType.Char) => LambdaFactory.CreateFunc((string _) => Convert.ToChar(_)), + (ScalarType.String, ScalarType.Enum) => LambdaFactory.CreateFunc((string _) => Enum.Parse(targetType, _, true)), + (ScalarType.String, ScalarType.Guid) => LambdaFactory.CreateFunc((string _) => Guid.Parse(_)), + (ScalarType.String, ScalarType.Index) => LambdaFactory.CreateFunc((string _) => Index.FromStart(int.Parse(_))), + (ScalarType.String, ScalarType.Uri) => LambdaFactory.CreateFunc((string _) => new Uri(_)), + (ScalarType.String, ScalarType.Boolean) => LambdaFactory.CreateFunc((string _) => bool.Parse(_)), + (ScalarType.String, ScalarType.SByte) => LambdaFactory.CreateFunc((string _) => sbyte.Parse(_)), + (ScalarType.String, ScalarType.Int16) => LambdaFactory.CreateFunc((string _) => short.Parse(_)), + (ScalarType.String, ScalarType.Int32) => LambdaFactory.CreateFunc((string _) => int.Parse(_)), + (ScalarType.String, ScalarType.Int64) => LambdaFactory.CreateFunc((string _) => long.Parse(_)), + (ScalarType.String, ScalarType.Int128) => LambdaFactory.CreateFunc((string _) => Int128.Parse(_)), + (ScalarType.String, ScalarType.BigInteger) => LambdaFactory.CreateFunc((string _) => BigInteger.Parse(_)), + (ScalarType.String, ScalarType.Byte) => LambdaFactory.CreateFunc((string _) => byte.Parse(_)), + (ScalarType.String, ScalarType.UInt16) => LambdaFactory.CreateFunc((string _) => ushort.Parse(_)), + (ScalarType.String, ScalarType.UInt32) => LambdaFactory.CreateFunc((string _) => uint.Parse(_)), + (ScalarType.String, ScalarType.UInt64) => LambdaFactory.CreateFunc((string _) => ulong.Parse(_)), + (ScalarType.String, ScalarType.UInt128) => LambdaFactory.CreateFunc((string _) => UInt128.Parse(_)), + (ScalarType.String, ScalarType.IntPtr) => LambdaFactory.CreateFunc((string _) => nint.Parse(_)), + (ScalarType.String, ScalarType.UIntPtr) => LambdaFactory.CreateFunc((string _) => nuint.Parse(_)), + (ScalarType.String, ScalarType.Half) => LambdaFactory.CreateFunc((string _) => Half.Parse(_)), + (ScalarType.String, ScalarType.Single) => LambdaFactory.CreateFunc((string _) => float.Parse(_)), + (ScalarType.String, ScalarType.Double) => LambdaFactory.CreateFunc((string _) => double.Parse(_)), + (ScalarType.String, ScalarType.Decimal) => LambdaFactory.CreateFunc((string _) => decimal.Parse(_)), + (ScalarType.String, ScalarType.DateOnly) => LambdaFactory.CreateFunc((string _) => DateOnly.Parse(_)), + (ScalarType.String, ScalarType.DateTime) => LambdaFactory.CreateFunc((string _) => DateTime.Parse(_)), + (ScalarType.String, ScalarType.DateTimeOffset) => LambdaFactory.CreateFunc((string _) => DateTimeOffset.Parse(_)), + (ScalarType.String, ScalarType.TimeOnly) => LambdaFactory.CreateFunc((string _) => TimeOnly.Parse(_)), + (ScalarType.String, ScalarType.TimeSpan) => LambdaFactory.CreateFunc((string _) => TimeSpan.Parse(_)), + + _ => null }; + if (expression is null) + return value.Cast(targetType); + if (isSourceNullable) - expression = expression.IsNotNull().IIf(expression, @this); + return expression.IsNotNull().IIf(expression, @this); return expression; } @@ -127,7 +177,7 @@ or ScalarType.DateOnly or ScalarType.DateTime or ScalarType.DateTimeOffset or Sc { string text when text.IsNotBlank() => bool.Parse(text), null or string => null, - '1' or 'x' or 'X' or 'y' or 'Y' or 't' or 'T' => true, + char x when TRUE_CHARS.Contains(x) => true, (sbyte)0 or (short)0 or 0 or 0L or (byte)0 or (ushort)0 or 0U or 0UL or 0F or 0D or 0M => false, sbyte or short or int or long or byte or ushort or uint or ulong or float or double or decimal => true, Int128 x => x != Int128.Zero, diff --git a/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj b/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj index 07634673..73d40e7f 100644 --- a/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj +++ b/tests/TypeCache.GraphQL.TestApp/TypeCache.GraphQL.TestApp.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/TypeCache.Tests/Extensions/EnumExtensions.cs b/tests/TypeCache.Tests/Extensions/EnumExtensions.cs index 776da077..e86168ab 100644 --- a/tests/TypeCache.Tests/Extensions/EnumExtensions.cs +++ b/tests/TypeCache.Tests/Extensions/EnumExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) 2021 Samuel Abraham using System; +using System.Linq; using TypeCache.Extensions; using Xunit; @@ -28,7 +29,7 @@ private enum TestEnum [Fact] public void Attributes() { - Assert.Equal(3, TestEnum.TestValue2.Attributes().Count); + Assert.Equal(3, TestEnum.TestValue2.Attributes().Count()); } [Fact] diff --git a/tests/TypeCache.Tests/TypeCache.Tests.csproj b/tests/TypeCache.Tests/TypeCache.Tests.csproj index 740e03bf..b8a5ab4b 100644 --- a/tests/TypeCache.Tests/TypeCache.Tests.csproj +++ b/tests/TypeCache.Tests/TypeCache.Tests.csproj @@ -9,10 +9,10 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/TypeCache.Tests/Utilities/EnumOfTests.cs b/tests/TypeCache.Tests/Utilities/EnumOfTests.cs deleted file mode 100644 index 08fc1b27..00000000 --- a/tests/TypeCache.Tests/Utilities/EnumOfTests.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2021 Samuel Abraham - -using System; -using System.Linq; -using System.Reflection; -using TypeCache.Extensions; -using TypeCache.Utilities; -using Xunit; - -namespace TypeCache.Tests.Utilities; - -public class EnumOfTests -{ - [Fact] - public void EnumOfBindingFlags() - { - Assert.Equal(1, EnumOf.Attributes.Count); - Assert.NotNull(EnumOf.Comparer); - Assert.True(EnumOf.Flags); - Assert.Equal(typeof(BindingFlags).Name(), EnumOf.Name); - Assert.Equal(Enum.GetValues(typeof(BindingFlags)), EnumOf.Tokens.Keys.ToArray()); - Assert.Equal(Enum.GetNames(typeof(BindingFlags)), EnumOf.Tokens.Values.Select(_ => _.Name).ToArray(), StringComparer.Ordinal); - Assert.Equal(ScalarType.Int32, EnumOf.UnderlyingType.GetDataType()); - Assert.Equal(typeof(int).TypeHandle, EnumOf.UnderlyingType.TypeHandle); - } - - [Fact] - public void EnumOfDataType() - { - Assert.Empty(EnumOf.Attributes); - Assert.NotNull(EnumOf.Comparer); - Assert.False(EnumOf.Flags); - Assert.Equal(typeof(ScalarType).Name(), EnumOf.Name); - Assert.Equal(Enum.GetValues(typeof(ScalarType)), EnumOf.Tokens.Keys.ToArray()); - Assert.Equal(Enum.GetNames(typeof(ScalarType)), EnumOf.Tokens.Values.Select(_ => _.Name).ToArray(), StringComparer.Ordinal); - Assert.Equal(ScalarType.Int32, EnumOf.UnderlyingType.GetDataType()); - Assert.Equal(typeof(int).TypeHandle, EnumOf.UnderlyingType.TypeHandle); - } -} diff --git a/tests/TypeCache.Tests/Utilities/EnumTests.cs b/tests/TypeCache.Tests/Utilities/EnumTests.cs new file mode 100644 index 00000000..cf513bef --- /dev/null +++ b/tests/TypeCache.Tests/Utilities/EnumTests.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2021 Samuel Abraham + +using System; +using System.Reflection; +using TypeCache.Extensions; +using TypeCache.Utilities; +using Xunit; + +namespace TypeCache.Tests.Utilities; + +public class EnumTests +{ + [Fact] + public void EnumOfBindingFlags() + { + Assert.Equal(typeof(BindingFlags).GetCustomAttributes(), Enum.Attributes); + Assert.True(Enum.Flags); + Assert.Equal(typeof(BindingFlags).Name, Enum.Name); + Assert.Equal(ScalarType.Int32, Enum.UnderlyingType.GetScalarType()); + Assert.Equal(typeof(int).TypeHandle, Enum.UnderlyingType.TypeHandle); + } + + [Fact] + public void EnumOfDataType() + { + Assert.Equal(typeof(ScalarType).GetCustomAttributes(), Enum.Attributes); + Assert.Empty(Enum.Attributes); + Assert.False(Enum.Flags); + Assert.Equal(typeof(ScalarType).Name, Enum.Name); + Assert.Equal(ScalarType.Int32, Enum.UnderlyingType.GetScalarType()); + Assert.Equal(typeof(int).TypeHandle, Enum.UnderlyingType.TypeHandle); + } +}