Skip to content

Commit 9a34ae4

Browse files
committed
add glob
1 parent 46bf573 commit 9a34ae4

14 files changed

+1487
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System.Runtime.CompilerServices;
2+
using Arbiter.Services.GlobMatching;
3+
4+
namespace Arbiter.Services;
5+
6+
/// <summary>
7+
/// Provides glob pattern matching functionality for file paths and strings.
8+
/// Supports wildcards (*, ?), character classes ([abc]), ranges ([a-z]), and brace expansion ({jpg,png}).
9+
/// </summary>
10+
public sealed class GlobMatcher
11+
{
12+
private readonly string _pattern;
13+
private readonly bool _caseSensitive;
14+
private readonly string? _patternUpper;
15+
private readonly StringComparison _stringComparison;
16+
private readonly PatternInfo _patternInfo;
17+
private readonly ComplexPatternMatcher? _complexMatcher;
18+
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="GlobMatcher"/> class.
21+
/// </summary>
22+
/// <param name="pattern">The glob pattern to match against.</param>
23+
/// <param name="caseSensitive">Whether matching should be case-sensitive (default: false).</param>
24+
public GlobMatcher(string pattern, bool caseSensitive = false)
25+
{
26+
_pattern = pattern ?? throw new ArgumentNullException(nameof(pattern));
27+
_caseSensitive = caseSensitive;
28+
29+
// Pre-compute string comparison mode once
30+
_stringComparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
31+
32+
// Pre-convert pattern to uppercase for case-insensitive matching
33+
if (!caseSensitive)
34+
{
35+
_patternUpper = pattern.ToUpperInvariant();
36+
}
37+
38+
// Pre-analyze pattern once at construction
39+
_patternInfo = PatternAnalyzer.Analyze(_pattern.AsSpan(), caseSensitive);
40+
41+
// Create complex matcher if needed
42+
if (_patternInfo.MatchMode == MatchMode.Complex)
43+
{
44+
_complexMatcher = new ComplexPatternMatcher(_pattern, _patternUpper, caseSensitive);
45+
}
46+
}
47+
48+
/// <summary>
49+
/// Determines whether the specified path matches the glob pattern.
50+
/// </summary>
51+
/// <param name="path">The path to test against the pattern.</param>
52+
/// <returns>true if the path matches the pattern; otherwise, false.</returns>
53+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
54+
public bool IsMatch(string path)
55+
{
56+
return IsMatch(path.AsSpan());
57+
}
58+
59+
/// <summary>
60+
/// Determines whether the specified path matches the glob pattern.
61+
/// </summary>
62+
/// <param name="path">The path to test against the pattern.</param>
63+
/// <returns>true if the path matches the pattern; otherwise, false.</returns>
64+
public bool IsMatch(ReadOnlySpan<char> path)
65+
{
66+
// Fast-path optimization for simple patterns
67+
if (_patternInfo.MatchMode != MatchMode.Complex)
68+
{
69+
return MatchSimplePattern(path);
70+
}
71+
72+
// Complex pattern matching
73+
return _complexMatcher?.Match(path) ?? false;
74+
}
75+
76+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
77+
private bool MatchSimplePattern(ReadOnlySpan<char> path)
78+
{
79+
return _patternInfo.MatchMode switch
80+
{
81+
MatchMode.Exact => SimplePatternMatcher.MatchExact(path, _patternInfo.LiteralValue!, _stringComparison),
82+
MatchMode.StartsWith => SimplePatternMatcher.MatchStartsWith(path, _patternInfo.LiteralValue!, _stringComparison),
83+
MatchMode.EndsWith => SimplePatternMatcher.MatchEndsWith(path, _patternInfo.LiteralValue!, _stringComparison),
84+
MatchMode.Contains => SimplePatternMatcher.MatchContains(path, _patternInfo.LiteralValue!, _stringComparison),
85+
_ => false
86+
};
87+
}
88+
}

0 commit comments

Comments
 (0)