diff --git a/sdmap/src/sdmap.ext.Dapper/sdmap.ext.Dapper.csproj b/sdmap/src/sdmap.ext.Dapper/sdmap.ext.Dapper.csproj index 796b482..7e5954e 100644 --- a/sdmap/src/sdmap.ext.Dapper/sdmap.ext.Dapper.csproj +++ b/sdmap/src/sdmap.ext.Dapper/sdmap.ext.Dapper.csproj @@ -5,6 +5,7 @@ sdmap.ext.Dapper sdmap.ext.Dapper dynamic sql;sdmap;dapper + true For more information, please view: https://github.com/sdcb/sdmap/blob/master/ReleaseNotes.md @@ -13,13 +14,13 @@ https://github.com/sdcb/sdmap/blob/master/ReleaseNotes.md false false false - 0.16.4 + 0.16.5 Dapper extensions for sdmap. https://github.com/sdcb/sdmap sdcb MIT - 0.16.4 - 0.16.4 + 0.16.5 + 0.16.5 LICENSE true true diff --git a/sdmap/src/sdmap.ext/sdmap.ext.csproj b/sdmap/src/sdmap.ext/sdmap.ext.csproj index ba1945c..4aaa91f 100644 --- a/sdmap/src/sdmap.ext/sdmap.ext.csproj +++ b/sdmap/src/sdmap.ext/sdmap.ext.csproj @@ -5,6 +5,7 @@ sdmap.ext sdmap.ext dynamic sql;sdmap + true For more information, please view: https://github.com/sdcb/sdmap/blob/master/ReleaseNotes.md @@ -13,13 +14,13 @@ https://github.com/sdcb/sdmap/blob/master/ReleaseNotes.md false false false - 0.16.4 + 0.16.5 Useful extensions for sdmap/Dapper. https://github.com/sdcb/sdmap sdcb MIT - 0.16.4 - 0.16.4 + 0.16.5 + 0.16.5 LICENSE true true diff --git a/sdmap/src/sdmap/Macros/Implements/RuntimeMacros.cs b/sdmap/src/sdmap/Macros/Implements/RuntimeMacros.cs index 052c841..d6cfd77 100644 --- a/sdmap/src/sdmap/Macros/Implements/RuntimeMacros.cs +++ b/sdmap/src/sdmap/Macros/Implements/RuntimeMacros.cs @@ -178,7 +178,7 @@ public static Result IsEqual(OneCallContext context, string syntax = (string)arguments[0]; var val = GetPropValue(self, syntax); var compare = arguments[1]; - if (IsEqual(val, compare)) + if (ValueComparer.Compare(val, compare) is ComparisonResult.AreEqual) return MacroUtil.EvalToString(arguments[2], context, self); return Empty; @@ -197,14 +197,14 @@ public static Result IsNotEqual(OneCallContext context, string syntax = (string)arguments[0]; var val = GetPropValue(self, syntax); var compare = arguments[1]; - if (!IsEqual(val, compare)) + if (ValueComparer.Compare(val, compare) is not ComparisonResult.AreEqual) return MacroUtil.EvalToString(arguments[2], context, self); return Empty; } [Macro("isLessThan")] - [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Number, SdmapTypes.StringOrSql)] + [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Any, SdmapTypes.StringOrSql)] public static Result IsLessThan(OneCallContext context, string ns, object self, object[] arguments) { @@ -214,16 +214,17 @@ public static Result IsLessThan(OneCallContext context, if (prop == null) return RequirePropNotNull(arguments[0]); string syntax = (string)arguments[0]; - var val = GetPropValue(self, syntax); - var compare = Convert.ToDouble(arguments[1]); - if (Convert.ToDouble(val) < compare) + var left = GetPropValue(self, syntax); + var right = arguments[1]; + + if (ValueComparer.Compare(left, right) is ComparisonResult.LeftIsLess) return MacroUtil.EvalToString(arguments[2], context, self); return Empty; } [Macro("isGreaterThan")] - [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Number, SdmapTypes.StringOrSql)] + [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Any, SdmapTypes.StringOrSql)] public static Result IsGreaterThan(OneCallContext context, string ns, object self, object[] arguments) { @@ -233,16 +234,17 @@ public static Result IsGreaterThan(OneCallContext context, if (prop == null) return RequirePropNotNull(arguments[0]); string syntax = (string)arguments[0]; - var val = GetPropValue(self, syntax); - var compare = Convert.ToDouble(arguments[1]); - if (Convert.ToDouble(val) > compare) + var left = GetPropValue(self, syntax); + var right = arguments[1]; + + if (ValueComparer.Compare(left, right) is ComparisonResult.LeftIsGreater) return MacroUtil.EvalToString(arguments[2], context, self); return Empty; } [Macro("isLessEqual")] - [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Number, SdmapTypes.StringOrSql)] + [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Any, SdmapTypes.StringOrSql)] public static Result IsLessEqual(OneCallContext context, string ns, object self, object[] arguments) { @@ -252,16 +254,17 @@ public static Result IsLessEqual(OneCallContext context, if (prop == null) return RequirePropNotNull(arguments[0]); string syntax = (string)arguments[0]; - var val = GetPropValue(self, syntax); - var compare = Convert.ToDouble(arguments[1]); - if (Convert.ToDouble(val) <= compare) + var left = GetPropValue(self, syntax); + var right = arguments[1]; + + if (ValueComparer.Compare(left, right) is ComparisonResult.LeftIsLess or ComparisonResult.AreEqual) return MacroUtil.EvalToString(arguments[2], context, self); return Empty; } [Macro("isGreaterEqual")] - [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Number, SdmapTypes.StringOrSql)] + [MacroArguments(SdmapTypes.Syntax, SdmapTypes.Any, SdmapTypes.StringOrSql)] public static Result IsGreaterEqual(OneCallContext context, string ns, object self, object[] arguments) { @@ -271,9 +274,10 @@ public static Result IsGreaterEqual(OneCallContext context, if (prop == null) return RequirePropNotNull(arguments[0]); string syntax = (string)arguments[0]; - var val = GetPropValue(self, syntax); - var compare = Convert.ToDouble(arguments[1]); - if (Convert.ToDouble(val) >= compare) + var left = GetPropValue(self, syntax); + var right = arguments[1]; + + if (ValueComparer.Compare(left, right) is ComparisonResult.LeftIsGreater or ComparisonResult.AreEqual) return MacroUtil.EvalToString(arguments[2], context, self); return Empty; @@ -477,40 +481,6 @@ public static object GetPropValue(object self, string prop) } } - public static bool IsEqual(object v1, object v2) - { - if (v1 is string str) - return str.Equals((string)v2); - - if (v1 is bool b) - return b.Equals((bool)v2); - - if (v1 is int integer) - return integer.Equals(Convert.ToInt32(v2)); - - if (v1 is long longValue) - return longValue.Equals(Convert.ToInt64(v2)); - - if (v1 is double db) - return db.Equals(Convert.ToDouble(v2)); - - if (v1 is decimal dm) - return dm.Equals(Convert.ToDecimal(v2)); - - if (v1 is DateTime date) - return date.Equals((DateTime)v2); - - if (v1 is Enum @enum) - { - if (v2 is string v2s) - return @enum.ToString() == v2s; - if (v2 is double v2d) - return Convert.ToInt64(@enum) == v2d; - } - - return v1 == v2; - } - public static bool IsEmpty(object v) { if (v == null) @@ -528,4 +498,4 @@ public static bool ArrayEmpty(IEnumerable arr) return !arr.GetEnumerator().MoveNext(); } } -} +} \ No newline at end of file diff --git a/sdmap/src/sdmap/Macros/Implements/ValueComparer.cs b/sdmap/src/sdmap/Macros/Implements/ValueComparer.cs new file mode 100644 index 0000000..8511ec8 --- /dev/null +++ b/sdmap/src/sdmap/Macros/Implements/ValueComparer.cs @@ -0,0 +1,140 @@ +using System; +using System.Linq; +using System.Reflection; +// ReSharper disable SuggestBaseTypeForParameter + +namespace sdmap.Macros.Implements; + +public enum ComparisonResult : byte +{ + Incomparable = 1, + AreEqual, + LeftIsGreater, + LeftIsLess +} + +internal static class ValueComparer +{ + public static ComparisonResult Compare(object left, object right) + => CompareImpl(left, right) switch + { + null => ComparisonResult.Incomparable, + 0 => ComparisonResult.AreEqual, + > 0 => ComparisonResult.LeftIsGreater, + < 0 => ComparisonResult.LeftIsLess + }; + + private static int? CompareImpl(object left, object right) + => left switch + { + null => right is null ? 0 : null, + not null when right is null => null, + + byte byteValue => byteValue.CompareTo(Convert.ToByte(right)), + sbyte sByteValue => sByteValue.CompareTo(Convert.ToSByte(right)), + short shortValue => shortValue.CompareTo(Convert.ToInt16(right)), + ushort ushortValue => ushortValue.CompareTo(Convert.ToUInt16(right)), + + int intValue => intValue.CompareTo(Convert.ToInt32(right)), + uint uIntValue => uIntValue.CompareTo(Convert.ToUInt32(right)), + long longValue => longValue.CompareTo(Convert.ToInt64(right)), + ulong uLongValue => uLongValue.CompareTo(Convert.ToUInt64(right)), + + float floatValue => floatValue.CompareTo(Convert.ToSingle(right)), + double doubleValue => doubleValue.CompareTo(Convert.ToDouble(right)), + decimal decimalValue => decimalValue.CompareTo(Convert.ToDecimal(right)), + + Enum enumValue => Compare(enumValue, right), + IComparable comparable => Compare(comparable, right), + + _ => null + }; + + private static int? Compare(Enum leftEnum, object right) + { + if (right is Enum rightEnum) + { + return leftEnum.GetType() == rightEnum.GetType() + ? leftEnum.CompareTo(rightEnum) + : null; + } + + return EnumParser.TryParse(leftEnum.GetType(), right.ToString(), out var parsed) + ? leftEnum.CompareTo(parsed) + : null; + } + + private static int? Compare(IComparable comparable, object right) + { + var leftType = comparable.GetType(); + + if (leftType == right.GetType()) + { + return comparable.CompareTo(right); + } + + return TryParse(leftType, right.ToString(), out var parsed) + ? comparable.CompareTo(parsed) + : null; + + static bool TryParse(Type targetType, string input, out object parsed) + { + if (targetType == typeof(Guid)) + { + var result = Guid.TryParse(input, out var parsedValue); + parsed = parsedValue; + return result; + } + + if (targetType == typeof(TimeSpan)) + { + var result = TimeSpan.TryParse(input, out var parsedValue); + parsed = parsedValue; + return result; + } + + if (targetType == typeof(DateTime)) + { + var result = DateTime.TryParse(input, out var parsedValue); + parsed = parsedValue; + return result; + } + + if (targetType == typeof(DateTimeOffset)) + { + var result = DateTimeOffset.TryParse(input, out var parsedValue); + parsed = parsedValue; + return result; + } + + parsed = null; + return false; + } + } + + private static class EnumParser + { + private static readonly MethodInfo TryParseMethod + = typeof(Enum) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(method => + method.Name == nameof(Enum.TryParse) + && method.IsGenericMethodDefinition + && method.ReturnType == typeof(bool) + && method.GetParameters().Length == 3 + && method.GetParameters().First().ParameterType == typeof(string) + ); + + public static bool TryParse(Type enumType, string value, out object parsed) + { + var parameters = new object[] { value, true, null }; + + var result = TryParseMethod + .MakeGenericMethod(enumType) + .Invoke(obj: null, parameters); + + parsed = parameters[2]; + return (bool)result; + } + } +} \ No newline at end of file diff --git a/sdmap/src/sdmap/sdmap.csproj b/sdmap/src/sdmap/sdmap.csproj index cccd557..166e4fc 100644 --- a/sdmap/src/sdmap/sdmap.csproj +++ b/sdmap/src/sdmap/sdmap.csproj @@ -3,9 +3,11 @@ A template engine for writing dynamic sql. net6;netstandard20 + latest sdmap sdmap dynamic sql;ibatis + true https://github.com/sdcb/sdmap/blob/master/ReleaseNotes.md git @@ -13,9 +15,9 @@ false false false - 0.16.4 - 0.16.4 - 0.16.4 + 0.16.5 + 0.16.5 + 0.16.5 sdcb MIT https://github.com/sdcb/sdmap diff --git a/sdmap/test/sdmap.test/ValueComparerTests.cs b/sdmap/test/sdmap.test/ValueComparerTests.cs new file mode 100644 index 0000000..41b019a --- /dev/null +++ b/sdmap/test/sdmap.test/ValueComparerTests.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using sdmap.Macros.Implements; +using Xunit; + +namespace sdmap.test; + +using static ComparisonResult; + +public class ValueComparerTests +{ + [Theory] + [MemberData(nameof(Compare_Cases_Number))] + [MemberData(nameof(Compare_Cases_Enum))] + [MemberData(nameof(Compare_Cases_Guid))] + [MemberData(nameof(Compare_Cases_Timespan))] + [MemberData(nameof(Compare_Cases_DateTime))] + public void Compare(object left, object right, ComparisonResult expected) + { + var actual = ValueComparer.Compare(left, right); + Assert.Equal(expected, actual); + } + + public static IEnumerable Compare_Cases_Number() + { + yield return new object[] { 0, 0, AreEqual }; + yield return new object[] { 0, 0L, AreEqual }; + yield return new object[] { (short) 1, 1L, AreEqual }; + yield return new object[] { (byte) 1, (ulong) 1, AreEqual }; + + yield return new object[] { 1D, 1F, AreEqual }; + yield return new object[] { 1M, 1M, AreEqual }; + yield return new object[] { 1M, 1D, AreEqual }; + yield return new object[] { 1M, 0D, LeftIsGreater }; + + yield return new object[] { 1M, "1", AreEqual }; + } + + public static IEnumerable Compare_Cases_Enum() + { + yield return new object[] { FirstEnum.One, FirstEnum.One, AreEqual }; + yield return new object[] { FirstEnum.One, FirstEnum.Two, LeftIsLess }; + yield return new object[] { FirstEnum.One, SecondEnum.One, Incomparable }; + yield return new object[] { FirstEnum.One, SecondEnum.Two, Incomparable }; + + yield return new object[] { FirstEnum.One, 1, AreEqual }; + yield return new object[] { FirstEnum.One, "1", AreEqual }; + yield return new object[] { FirstEnum.One, "One", AreEqual }; + yield return new object[] { FirstEnum.One, 1.0M, Incomparable }; + } + + public static IEnumerable Compare_Cases_Guid() + { + yield return new object[] + { + new Guid("01d1692d-2221-47cd-892b-4a6552c6a9cc"), + new Guid("01d1692d-2221-47cd-892b-4a6552c6a9cc"), + AreEqual + }; + + yield return new object[] + { + new Guid("01d1692d-2221-47cd-892b-4a6552c6a9cc"), + "01d1692d-2221-47cd-892b-4a6552c6a9cc", + AreEqual + }; + + yield return new object[] + { + new Guid("00000000-0000-0000-0000-000000000000"), + new Guid("11111111-0000-0000-0000-000000000000"), + LeftIsLess + }; + + yield return new object[] + { + new Guid("01d1692d-2221-47cd-892b-4a6552c6a9cc"), + DateTime.Now, + Incomparable + }; + } + + public static IEnumerable Compare_Cases_Timespan() + { + yield return new object[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), AreEqual }; + yield return new object[] { TimeSpan.FromSeconds(1), "00:00:01", AreEqual }; + yield return new object[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(0), LeftIsGreater }; + yield return new object[] { TimeSpan.FromSeconds(1), "ten seconds", Incomparable }; + yield return new object[] { TimeSpan.FromSeconds(1), DateTime.Now, Incomparable }; + } + + public static IEnumerable Compare_Cases_DateTime() + { + yield return new object[] { new DateTime(2023, 01, 01), new DateTime(2023, 01, 01), AreEqual }; + yield return new object[] { new DateTime(2023, 01, 01), new DateTime(1900, 01, 01), LeftIsGreater }; + yield return new object[] { new DateTime(2023, 01, 01), "2023-01-01", AreEqual }; + yield return new object[] { DateTime.Now, "today", Incomparable }; + } + + private enum FirstEnum { One = 1, Two = 2 } + + private enum SecondEnum { One = 1, Two = 2 } +} \ No newline at end of file