Skip to content

Commit

Permalink
Merge branch 'main' of github.com:panoramicdata/PanoramicData.NCalcEx…
Browse files Browse the repository at this point in the history
…tensions
  • Loading branch information
TheCakeMonster committed Oct 10, 2024
2 parents 2fbde33 + f20f890 commit 76f06ee
Show file tree
Hide file tree
Showing 22 changed files with 195 additions and 100 deletions.
8 changes: 4 additions & 4 deletions PanoramicData.NCalcExtensions.Test/DateAddTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class DateAddTests : NCalcTest
[InlineData("2023-12-05T05:00:01Z", 1, "days", "2023-12-06T05:00:01Z")]
[InlineData("2023-12-05T05:00:01Z", 1, "months", "2024-01-05T05:00:01Z")]
[InlineData("2023-12-05T05:00:01Z", 1, "years", "2024-12-05T05:00:01Z")]
public void DateAdd_ParameterisedInput_GivesExpectedOutput(string initialDateAndTime, int quantity, string units, string expectedDateAndTime)
public void DateAdd_ParameterizedInput_GivesExpectedOutput(string initialDateAndTime, int quantity, string units, string expectedDateAndTime)
{
var recognised = DateTime.TryParse(initialDateAndTime, out var initialDateTime);
recognised.Should().BeTrue();
Expand All @@ -35,7 +35,7 @@ public void DateAdd_ParameterisedInput_GivesExpectedOutput(string initialDateAnd
[InlineData("2023-12-05T05:00:01Z", 250, "aa")]
[InlineData("2023-12-05T05:00:01Z", 1, "nanoseconds")]
[InlineData("2023-12-05T05:00:01Z", 1, "weeks")]
public void DateAdd_Unknownunits_ThrowsFormatException(string initialDateAndTime, int quantity, string units)
public void DateAdd_UnknownUnits_ThrowsFormatException(string initialDateAndTime, int quantity, string units)
{
var recognised = DateTime.TryParse(initialDateAndTime, out var initialDateTime);
recognised.Should().BeTrue();
Expand Down Expand Up @@ -66,7 +66,7 @@ public void DateAdd_SubtractionBeyondMinDateTime_ThrowsArgumentOutOfRangeExcepti
}

[Fact]
public void DateAdd_IncorrectunitsDataType_ThrowsFormatException()
public void DateAdd_IncorrectUnitsDataType_ThrowsFormatException()
{
var units = 1;
var quantity = 1;
Expand All @@ -82,7 +82,7 @@ public void DateAdd_IncorrectunitsDataType_ThrowsFormatException()
}

[Fact]
public void DateAdd_IncorrectquantityDataType_ThrowsFormatException()
public void DateAdd_IncorrectQuantityDataType_ThrowsFormatException()
{
var units = "Hours";
var quantity = "Hours";
Expand Down
1 change: 0 additions & 1 deletion PanoramicData.NCalcExtensions.Test/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
global using FluentAssertions;
global using NCalc;
global using Newtonsoft.Json.Linq;
global using PanoramicData.NCalcExtensions.Exceptions;
global using System;
Expand Down
4 changes: 2 additions & 2 deletions PanoramicData.NCalcExtensions.Test/HumanizeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ public class HumanizeTests
[InlineData("1", "Hours", "1 hour")]
[InlineData("1", "Days", "1 day")]
[InlineData("1", "Weeks", "7 days")]
public void Humanize_UsingInlineData_MatchesExpectedValue(string expressionText, string datatype, string expected)
public void Humanize_UsingInlineData_MatchesExpectedValue(string expressionText, string dataType, string expected)
{
var expression = new ExtendedExpression($"humanize({expressionText},'{datatype}')");
var expression = new ExtendedExpression($"humanize({expressionText},'{dataType}')");
var result = expression.Evaluate();
result.Should().Be(expected);
}
Expand Down
7 changes: 7 additions & 0 deletions PanoramicData.NCalcExtensions.Test/ReplaceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,11 @@ public void Replace_Example2_Succeeds()
var expression = new ExtendedExpression("replace('abcdefg', 'cde', '')");
(expression.Evaluate() as string).Should().Be("abfg");
}

[Fact]
public void Replace_Example3_Succeeds()
{
var expression = new ExtendedExpression("replace('abcdefg', 'a', '1', 'bc', '23')");
(expression.Evaluate() as string).Should().Be("123defg");
}
}
19 changes: 19 additions & 0 deletions PanoramicData.NCalcExtensions.Test/ToUpperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,22 @@ public void ToUpper_UsingInlineData_ResultMatchExpectedValue(string input, strin
result.Should().Be(expected);
}
}

public class TrimTests
{
[Theory]
[InlineData(" ", " ")]
[InlineData("\r", "")]
[InlineData("", "\r")]
[InlineData("\t", "\r")]
[InlineData(" \n", "\r ")]

public void Trim_UsingInlineData_ResultMatchExpectedValue(string prefix, string suffix)
{
var input = $"{prefix}test{suffix}";
var expression = new ExtendedExpression($"trim(x)");
expression.Parameters.Add("x", input);
var result = expression.Evaluate();
result.Should().Be("test");
}
}
1 change: 1 addition & 0 deletions PanoramicData.NCalcExtensions.sln.vsspell.dic
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ regex
sbyte
sdf
Silverlight
snupkg
ss
startswith
tolower
Expand Down
30 changes: 20 additions & 10 deletions PanoramicData.NCalcExtensions/ExtendedExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ namespace PanoramicData.NCalcExtensions;
public class ExtendedExpression : Expression
{
private readonly Dictionary<string, object?> _storageDictionary = [];
private readonly CultureInfo _cultureInfo;
internal const string StorageDictionaryParameterName = "__storageDictionary";

public ExtendedExpression(string expression) : base(expression)
public ExtendedExpression(string expression) : this(expression, EvaluateOptions.None, CultureInfo.InvariantCulture) { }

public ExtendedExpression(
string expression,
EvaluateOptions evaluateOptions,
CultureInfo cultureInfo) : base(expression, evaluateOptions, cultureInfo)
{
_cultureInfo = cultureInfo;
Parameters[StorageDictionaryParameterName] = _storageDictionary;
EvaluateFunction += Extend;
CacheEnabled = false;
Expand Down Expand Up @@ -72,7 +79,7 @@ internal void Extend(string functionName, FunctionArgs functionArgs)
Capitalize.Evaluate(functionArgs);
return;
case ExtensionFunction.Cast:
Cast.Evaluate(functionArgs);
Cast.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.ChangeTimeZone:
ChangeTimeZone.Evaluate(functionArgs);
Expand All @@ -96,13 +103,13 @@ internal void Extend(string functionName, FunctionArgs functionArgs)
DateAddMethods.Evaluate(functionArgs);
return;
case ExtensionFunction.DateTime:
DateTimeMethods.Evaluate(functionArgs);
DateTimeMethods.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.DateTimeAsEpoch:
DateTimeAsEpoch.Evaluate(functionArgs);
DateTimeAsEpoch.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.DateTimeAsEpochMs:
DateTimeAsEpochMs.Evaluate(functionArgs);
DateTimeAsEpochMs.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.DateTimeIsInPast:
DateTimeIsInPast.Evaluate(functionArgs);
Expand All @@ -129,7 +136,7 @@ internal void Extend(string functionName, FunctionArgs functionArgs)
FirstOrDefault.Evaluate(functionArgs);
return;
case ExtensionFunction.Format:
Format.Evaluate(functionArgs);
Format.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.GetProperty:
GetProperty.Evaluate(functionArgs);
Expand Down Expand Up @@ -196,7 +203,7 @@ internal void Extend(string functionName, FunctionArgs functionArgs)
List.Evaluate(functionArgs);
return;
case ExtensionFunction.ListOf:
ListOf.Evaluate(functionArgs);
ListOf.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.Max:
Max.Evaluate(functionArgs);
Expand Down Expand Up @@ -292,20 +299,23 @@ internal void Extend(string functionName, FunctionArgs functionArgs)
throw Throw.Evaluate(functionArgs);
case ExtensionFunction.TimeSpan:
case ExtensionFunction.TimeSpanCamel:
Extensions.TimeSpan.Evaluate(functionArgs);
Extensions.TimeSpan.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.ToDateTime:
ToDateTime.Evaluate(functionArgs);
ToDateTime.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.ToLower:
ToLower.Evaluate(functionArgs);
return;
case ExtensionFunction.ToString:
Extensions.ToString.Evaluate(functionArgs);
Extensions.ToString.Evaluate(functionArgs, _cultureInfo);
return;
case ExtensionFunction.ToUpper:
ToUpper.Evaluate(functionArgs);
return;
case ExtensionFunction.Trim:
Trim.Evaluate(functionArgs);
return;
case ExtensionFunction.Try:
Try.Evaluate(functionArgs);
return;
Expand Down
1 change: 1 addition & 0 deletions PanoramicData.NCalcExtensions/ExtensionFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public static class ExtensionFunction
public const string ToLower = "toLower";
public new const string ToString = "toString";
public const string ToUpper = "toUpper";
public const string Trim = "trim";
public const string Try = "try";
public const string TryParse = "tryParse";
public const string TypeOf = "typeOf";
Expand Down
4 changes: 2 additions & 2 deletions PanoramicData.NCalcExtensions/Extensions/Cast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ string typeName

internal static class Cast
{
internal static void Evaluate(FunctionArgs functionArgs)
internal static void Evaluate(FunctionArgs functionArgs, CultureInfo cultureInfo)
{
const int castParameterCount = 2;
if (functionArgs.Parameters.Length != castParameterCount)
Expand All @@ -36,7 +36,7 @@ internal static void Evaluate(FunctionArgs functionArgs)
var castType = Type.GetType(castTypeString)
?? throw new ArgumentException($"{ExtensionFunction.Cast} function - Expected second argument to be a valid .NET type e.g. System.Decimal.");

var result = Convert.ChangeType(inputObject, castType, CultureInfo.InvariantCulture);
var result = Convert.ChangeType(inputObject, castType, cultureInfo);
functionArgs.Result = result;
return;
}
Expand Down
4 changes: 2 additions & 2 deletions PanoramicData.NCalcExtensions/Extensions/DateTimeAsEpoch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ string format

internal static class DateTimeAsEpoch
{
internal static void Evaluate(FunctionArgs functionArgs)
internal static void Evaluate(FunctionArgs functionArgs, CultureInfo cultureInfo)
{
var dateTimeOffset = DateTimeOffset.ParseExact(
functionArgs.Parameters[0].Evaluate() as string, // Input date as string
functionArgs.Parameters[1].Evaluate() as string,
CultureInfo.InvariantCulture.DateTimeFormat,
cultureInfo.DateTimeFormat,
DateTimeStyles.AssumeUniversal);
functionArgs.Result = dateTimeOffset.ToUnixTimeSeconds();
}
Expand Down
4 changes: 2 additions & 2 deletions PanoramicData.NCalcExtensions/Extensions/DateTimeAsEpochMs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ string format

internal static class DateTimeAsEpochMs
{
internal static void Evaluate(FunctionArgs functionArgs)
internal static void Evaluate(FunctionArgs functionArgs, CultureInfo cultureInfo)
{
var dateTimeOffset = DateTimeOffset.ParseExact(
functionArgs.Parameters[0].Evaluate() as string, // Input date as string
functionArgs.Parameters[1].Evaluate() as string,
CultureInfo.InvariantCulture.DateTimeFormat,
cultureInfo.DateTimeFormat,
DateTimeStyles.AssumeUniversal);
functionArgs.Result = dateTimeOffset.ToUnixTimeMilliseconds();
}
Expand Down
18 changes: 9 additions & 9 deletions PanoramicData.NCalcExtensions/Extensions/DateTimeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ int secondOffset

internal static class DateTimeMethods
{
internal static void Evaluate(FunctionArgs functionArgs)
internal static void Evaluate(FunctionArgs functionArgs, CultureInfo cultureInfo)
{
if (functionArgs.Parameters.Length > 0)
{
Expand Down Expand Up @@ -108,7 +108,7 @@ internal static void Evaluate(FunctionArgs functionArgs)
.AddHours(hoursToAdd)
.AddMinutes(minutesToAdd)
.AddSeconds(secondsToAdd)
.ToString(format, CultureInfo.InvariantCulture);
.ToString(format, cultureInfo);
}

private static double? GetNullableDouble(Expression expression)
Expand All @@ -119,15 +119,15 @@ internal static void Evaluate(FunctionArgs functionArgs)
_ => null,
};

internal static string BetterToString(this DateTime dateTime, string format)
internal static string BetterToString(this DateTime dateTime, string format, CultureInfo cultureInfo)
=> format switch
{
"dayOfYear" => dateTime.DayOfYear.ToString(),
"weekOfMonth" => dateTime.WeekOfMonth().ToString(),
"dayOfYear" => dateTime.DayOfYear.ToString(cultureInfo),
"weekOfMonth" => dateTime.WeekOfMonth().ToString(cultureInfo),
"weekOfMonthText" => GetWeekText(dateTime.WeekOfMonth()),
"weekDayOfMonth" => dateTime.WeekDayOfMonth().ToString(),
"weekDayOfMonth" => dateTime.WeekDayOfMonth().ToString(cultureInfo),
"weekDayOfMonthText" => GetWeekText(dateTime.WeekDayOfMonth()),
_ => dateTime.ToString(format, CultureInfo.InvariantCulture)
_ => dateTime.ToString(format, cultureInfo)
};

private static string GetWeekText(int weekOfMonth) => weekOfMonth switch
Expand All @@ -150,10 +150,10 @@ public static int WeekOfMonth(this DateTime dateTime)
public static int WeekDayOfMonth(this DateTime dateTime)
=> ((dateTime.Day - 1) / 7) + 1;

internal static string ToDateTimeInTargetTimeZone(this DateTime dateTime, string formatFormat, string timeZoneString)
internal static string ToDateTimeInTargetTimeZone(this DateTime dateTime, string formatFormat, string timeZoneString, CultureInfo cultureInfo)
{
var timeZoneInfo = TZConvert.GetTimeZoneInfo(timeZoneString);
var dateTimeOffset = new DateTimeOffset(dateTime, -timeZoneInfo.GetUtcOffset(dateTime));
return dateTimeOffset.UtcDateTime.BetterToString(formatFormat);
return dateTimeOffset.UtcDateTime.BetterToString(formatFormat, cultureInfo);
}
}
22 changes: 11 additions & 11 deletions PanoramicData.NCalcExtensions/Extensions/Format.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ string Format(

internal static class Format
{
internal static void Evaluate(FunctionArgs functionArgs)
internal static void Evaluate(FunctionArgs functionArgs, CultureInfo cultureInfo)
{
const int min = 2;
const int max = 3;
Expand All @@ -44,35 +44,35 @@ internal static void Evaluate(FunctionArgs functionArgs)

functionArgs.Result = (inputObject, functionArgs.Parameters.Length) switch
{
(int inputInt, 2) => inputInt.ToString(formatFormat, CultureInfo.InvariantCulture),
(double inputDouble, 2) => inputDouble.ToString(formatFormat, CultureInfo.InvariantCulture),
(DateTime dateTime, 2) => dateTime.BetterToString(formatFormat),
(DateTime dateTime, 3) => dateTime.ToDateTimeInTargetTimeZone(formatFormat, functionArgs.Parameters[2].Evaluate() as string ?? throw new ArgumentException($"{ExtensionFunction.Format} function - expected third argument to be a TimeZone string")),
(string inputString, 2) => GetThing(inputString, formatFormat),
(int inputInt, 2) => inputInt.ToString(formatFormat, cultureInfo),
(double inputDouble, 2) => inputDouble.ToString(formatFormat, cultureInfo),
(DateTime dateTime, 2) => dateTime.BetterToString(formatFormat, cultureInfo),
(DateTime dateTime, 3) => dateTime.ToDateTimeInTargetTimeZone(formatFormat, functionArgs.Parameters[2].Evaluate() as string ?? throw new ArgumentException($"{ExtensionFunction.Format} function - expected third argument to be a TimeZone string"), cultureInfo),
(string inputString, 2) => GetThing(inputString, formatFormat, cultureInfo),
_ => throw new NotSupportedException($"Unsupported input type {inputObject.GetType().Name} or incorrect number of parameters.")
};
}

private static string GetThing(string inputString, string formatFormat)
private static string GetThing(string inputString, string formatFormat, CultureInfo cultureInfo)
{
// Assume this is a number
if (long.TryParse(inputString, out var longValue))
{
return longValue.ToString(formatFormat, CultureInfo.InvariantCulture);
return longValue.ToString(formatFormat, cultureInfo);
}

if (double.TryParse(inputString, out var doubleValue))
{
return doubleValue.ToString(formatFormat, CultureInfo.InvariantCulture);
return doubleValue.ToString(formatFormat, cultureInfo);
}

if (DateTimeOffset.TryParse(
inputString,
CultureInfo.InvariantCulture.DateTimeFormat,
cultureInfo.DateTimeFormat,
DateTimeStyles.AssumeUniversal,
out var dateTimeOffsetValue))
{
return dateTimeOffsetValue.UtcDateTime.BetterToString(formatFormat);
return dateTimeOffsetValue.UtcDateTime.BetterToString(formatFormat, cultureInfo);
}

throw new FormatException($"Could not parse '{inputString}' as a number or date.");
Expand Down
Loading

0 comments on commit 76f06ee

Please sign in to comment.