Skip to content

Commit a5f8525

Browse files
committed
ability to parse to other primitive numeric types
1 parent 0bc8c68 commit a5f8525

File tree

3 files changed

+86
-3
lines changed

3 files changed

+86
-3
lines changed

src/GoatQuery/src/Evaluator/FilterEvaluator.cs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq.Expressions;
34
using FluentResults;
@@ -18,21 +19,27 @@ public static Result<Expression> Evaluate(QueryExpression expression, ParameterE
1819

1920
var property = Expression.Property(parameterExpression, propertyName);
2021

21-
ConstantExpression value = null;
22+
ConstantExpression value;
2223

2324
switch (exp.Right)
2425
{
2526
case GuidLiteral literal:
2627
value = Expression.Constant(literal.Value, property.Type);
2728
break;
2829
case IntegerLiteral literal:
29-
value = Expression.Constant(literal.Value, property.Type);
30+
var integerConstant = GetIntegerExpressionConstant(literal.Value, property.Type);
31+
if (integerConstant.IsFailed)
32+
{
33+
return Result.Fail(integerConstant.Errors);
34+
}
35+
36+
value = integerConstant.Value;
3037
break;
3138
case StringLiteral literal:
3239
value = Expression.Constant(literal.Value, property.Type);
3340
break;
3441
default:
35-
break;
42+
return Result.Fail($"Unsupported literal type: {exp.Right.GetType().Name}");
3643
}
3744

3845
switch (exp.Operator)
@@ -47,6 +54,8 @@ public static Result<Expression> Evaluate(QueryExpression expression, ParameterE
4754
var method = identifier.Value.GetType().GetMethod("Contains", new[] { value?.Value.GetType() });
4855

4956
return Expression.Call(property, method, value);
57+
default:
58+
return Result.Fail($"Unsupported operator: {exp.Operator}");
5059
}
5160
}
5261

@@ -75,4 +84,37 @@ public static Result<Expression> Evaluate(QueryExpression expression, ParameterE
7584

7685
return null;
7786
}
87+
88+
private static Result<ConstantExpression> GetIntegerExpressionConstant(int value, Type targetType)
89+
{
90+
try
91+
{
92+
// Fetch the underlying type if it's nullable.
93+
var underlyingType = Nullable.GetUnderlyingType(targetType);
94+
var type = underlyingType ?? targetType;
95+
96+
object convertedValue = type switch
97+
{
98+
Type t when t == typeof(int) => value,
99+
Type t when t == typeof(long) => Convert.ToInt64(value),
100+
Type t when t == typeof(short) => Convert.ToInt16(value),
101+
Type t when t == typeof(byte) => Convert.ToByte(value),
102+
Type t when t == typeof(uint) => Convert.ToUInt32(value),
103+
Type t when t == typeof(ulong) => Convert.ToUInt64(value),
104+
Type t when t == typeof(ushort) => Convert.ToUInt16(value),
105+
Type t when t == typeof(sbyte) => Convert.ToSByte(value),
106+
_ => throw new NotSupportedException($"Unsupported numeric type: {targetType.Name}")
107+
};
108+
109+
return Expression.Constant(convertedValue, targetType);
110+
}
111+
catch (OverflowException)
112+
{
113+
return Result.Fail($"Value {value} is too large for type {targetType.Name}");
114+
}
115+
catch (Exception ex)
116+
{
117+
return Result.Fail($"Error converting {value} to {targetType.Name}: {ex.Message}");
118+
}
119+
}
78120
}

src/GoatQuery/tests/Filter/FilterTest.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,43 @@ public void Test_Filter_WithCustomJsonPropertyName()
156156
new CustomJsonPropertyUser { Lastname = "John" },
157157
}, result.Value.Query);
158158
}
159+
160+
public record IntegerConverts
161+
{
162+
public long Long { get; set; }
163+
public short Short { get; set; }
164+
public byte Byte { get; set; }
165+
public uint Uint { get; set; }
166+
public ulong ULong { get; set; }
167+
public ushort UShort { get; set; }
168+
public sbyte SByte { get; set; }
169+
}
170+
171+
[Theory]
172+
[InlineData("long eq 10")]
173+
[InlineData("short eq 20")]
174+
[InlineData("byte eq 30")]
175+
[InlineData("uint eq 40")]
176+
[InlineData("ulong eq 50")]
177+
[InlineData("ushort eq 60")]
178+
[InlineData("sbyte eq 70")]
179+
public void Test_Filter_CanConvertIntToOtherNumericTypes(string filter)
180+
{
181+
var users = new List<IntegerConverts>{
182+
new IntegerConverts() { Long = 0, Short = 0, Byte = 0, Uint = 0, ULong = 0, UShort = 0, SByte = 0},
183+
new IntegerConverts() { Long = 10, Short = 20, Byte = 30, Uint = 40, ULong = 50, UShort = 60, SByte = 70},
184+
new IntegerConverts() { Long = 1, Short = 2, Byte = 3, Uint = 4, ULong = 5, UShort = 6, SByte = 7},
185+
}.AsQueryable();
186+
187+
var query = new Query
188+
{
189+
Filter = filter
190+
};
191+
192+
var result = users.Apply(query);
193+
194+
Assert.Equal(new List<IntegerConverts>{
195+
new IntegerConverts() { Long = 10, Short = 20, Byte = 30, Uint = 40, ULong = 50, UShort = 60, SByte = 70},
196+
}, result.Value.Query);
197+
}
159198
}

src/GoatQuery/tests/User.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ public record User
55
public int Age { get; set; }
66
public Guid UserId { get; set; }
77
public string Firstname { get; set; } = string.Empty;
8+
9+
public string Long { get; set; } = string.Empty;
810
}
911

1012
public sealed record CustomJsonPropertyUser : User

0 commit comments

Comments
 (0)