Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ _NCrunch_*
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
**/.idea/**/modules.xml
.idea/.idea.Exceptionless.DateTimeExtensions/.idea/
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Text.RegularExpressions;

namespace Exceptionless.DateTimeExtensions.FormatParsers.PartParsers;

[Priority(1)]
public class WildcardPartParser : IPartParser
{
private static readonly Regex _wildCardRegex = new(@"\G\s*\*(?=\s|\]|\}|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

public Regex Regex => _wildCardRegex;

public DateTimeOffset? Parse(Match match, DateTimeOffset relativeBaseTime, bool isUpperLimit)
{
if (!match.Success)
return null;

return isUpperLimit ? DateTimeOffset.MaxValue : DateTimeOffset.MinValue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ namespace Exceptionless.DateTimeExtensions.FormatParsers;
[Priority(25)]
public class TwoPartFormatParser : IFormatParser
{
private static readonly Regex _beginRegex = new(@"^\s*");
private static readonly Regex _beginRegex = new(@"^\s*(?:[\[\{])?\s*");
private static readonly Regex _delimiterRegex = new(@"\G(?:\s*-\s*|\s+TO\s+)", RegexOptions.IgnoreCase);
private static readonly Regex _endRegex = new(@"\G\s*$");
private static readonly Regex _endRegex = new(@"\G\s*(?:[\]\}])?\s*$");

public TwoPartFormatParser()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Exceptionless.DateTimeExtensions.FormatParsers.PartParsers;
using Microsoft.Extensions.Logging;
using Xunit;
using Xunit.Abstractions;

namespace Exceptionless.DateTimeExtensions.Tests.FormatParsers.PartParsers;

public class WildcardPartParserTests : PartParserTestsBase
{
public WildcardPartParserTests(ITestOutputHelper output) : base(output) { }

[Theory]
[MemberData(nameof(ParseInputs))]
public void ParseInput(string input, bool isUpperLimit, DateTimeOffset? expected)
{
var parser = new WildcardPartParser();
_logger.LogInformation("Testing input: '{Input}', IsUpperLimit: {IsUpperLimit}, Expected: {Expected}", input, isUpperLimit, expected);

var match = parser.Regex.Match(input);
_logger.LogInformation("Regex match success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", match.Success, match.Value, match.Index, match.Length);

var result = parser.Parse(match, _now, isUpperLimit);
_logger.LogInformation("Parse result: {Result}", result);

if (expected == null)
{
Assert.Null(result);
}
else
{
Assert.NotNull(result);
Assert.Equal(expected.Value.DateTime, result.Value.DateTime);
}
}

public static IEnumerable<object[]> ParseInputs
{
get
{
return new[]
{
// Valid wildcard inputs
new object[] { "*", false, DateTimeOffset.MinValue },
new object[] { "*", true, DateTimeOffset.MaxValue },
new object[] { " * ", false, DateTimeOffset.MinValue },
new object[] { " * ", true, DateTimeOffset.MaxValue },
new object[] { " * ", false, DateTimeOffset.MinValue },
new object[] { " * ", true, DateTimeOffset.MaxValue },

// Invalid inputs (patterns that should not match a complete wildcard)
new object[] { "blah", false, null },
new object[] { "blah", true, null },
new object[] { "2012", false, null },
new object[] { "2012", true, null },
new object[] { "**", false, null },

// This should match the first * in a two-part context like "* *"
new object[] { "* *", false, DateTimeOffset.MinValue },
};
}
}

[Fact]
public void RegexPatternTest()
{
var parser = new WildcardPartParser();
var regex = parser.Regex;

_logger.LogInformation("Regex pattern: {Pattern}", regex);

// Test various inputs
var testInputs = new[] { "*", " * ", " * ", "blah", "2012", "**", "* *", "" };

foreach (var input in testInputs)
{
var match = regex.Match(input);
_logger.LogInformation("Input: '{Input}' -> Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", input, match.Success, match.Value, match.Index, match.Length);
}
}

[Fact]
public void TestInTwoPartContext()
{
var parser = new WildcardPartParser();

// Test how it behaves in a two-part parsing context
var inputs = new[] { "* TO 2013", "2012 TO *", "[* TO 2013]", "{2012 TO *}" };

foreach (var input in inputs)
{
_logger.LogInformation("Testing two-part context for: '{Input}'", input);

// Test parsing at the beginning
var match = parser.Regex.Match(input, 0);
_logger.LogInformation(" At position 0: Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", match.Success, match.Value, match.Index, match.Length);

// Test parsing after bracket
if (input.StartsWith("[") || input.StartsWith("{"))
{
match = parser.Regex.Match(input, 1);
_logger.LogInformation(" At position 1: Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", match.Success, match.Value, match.Index, match.Length);
}

// Find TO and test parsing after it
var toIndex = input.IndexOf(" TO ", StringComparison.OrdinalIgnoreCase);
if (toIndex >= 0)
{
var afterTo = toIndex + 4;
match = parser.Regex.Match(input, afterTo);
_logger.LogInformation(" After TO at position {Position}: Success: {Success}, Value: '{Value}', Index: {Index}, Length: {Length}", afterTo, match.Success, match.Value, match.Index, match.Length);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,32 @@ public static IEnumerable<object[]> Inputs
get
{
return new[] {
// Original dash delimiter syntax
new object[] { "2012-2013", _now.ChangeYear(2012).StartOfYear(), _now.ChangeYear(2013).EndOfYear() },
new object[] { "5 days ago - now", _now.SubtractDays(5).StartOfDay(), _now },
new object[] { "jan-feb", _now.ChangeMonth(1).StartOfMonth(), _now.ChangeMonth(2).EndOfMonth() },
new object[] { "now-this feb", _now, _now.AddYears(1).ChangeMonth(2).EndOfMonth() },

// TO delimiter syntax (case-insensitive)
new object[] { "2012 TO 2013", _now.ChangeYear(2012).StartOfYear(), _now.ChangeYear(2013).EndOfYear() },
new object[] { "jan to feb", _now.ChangeMonth(1).StartOfMonth(), _now.ChangeMonth(2).EndOfMonth() },
new object[] { "5 days ago TO now", _now.SubtractDays(5).StartOfDay(), _now },

// Elasticsearch bracket syntax
new object[] { "[2012 TO 2013]", _now.ChangeYear(2012).StartOfYear(), _now.ChangeYear(2013).EndOfYear() },
new object[] { "{jan TO feb}", _now.ChangeMonth(1).StartOfMonth(), _now.ChangeMonth(2).EndOfMonth() },
new object[] { "[2012-2013]", _now.ChangeYear(2012).StartOfYear(), _now.ChangeYear(2013).EndOfYear() },

// Wildcard support
new object[] { "* TO 2013", DateTime.MinValue, _now.ChangeYear(2013).EndOfYear() },
new object[] { "2012 TO *", _now.ChangeYear(2012).StartOfYear(), DateTime.MaxValue},
new object[] { "[* TO 2013]", DateTime.MinValue, _now.ChangeYear(2013).EndOfYear() },
new object[] { "{2012 TO *}", _now.ChangeYear(2012).StartOfYear(), DateTime.MaxValue },

// Invalid inputs
new object[] { "blah", null, null },
new object[] { "blah blah", null, null }
new object[] { "[invalid", null, null },
new object[] { "invalid}", null, null }
};
}
}
Expand Down