This commit is contained in:
parent
fdf1019b7a
commit
4eaa3c6d22
5 changed files with 116 additions and 259 deletions
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace Geekeey.SemVer.Tests;
|
||||
|
||||
internal sealed class SemanticVersionRangeTests
|
||||
|
|
@ -119,6 +117,19 @@ internal sealed class SemanticVersionRangeTests
|
|||
await Assert.That(range.ToString("n", null)).IsEqualTo(">=1.0.0 !=2.0.0");
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Arguments("^5.*", "5.1.1", "6.1.0", ">=5.0.0 <6.0.0")]
|
||||
[Arguments("5.*", "5.1.1", "6.1.0", ">=5.0.0 <6.0.0")]
|
||||
[Arguments(">=5.*", "5.1.1", "6.1.0", ">=5.0.0 <6.0.0")]
|
||||
public async Task I_can_handle_ranges_with_wildcard(string range, string inside, string outside, string expected)
|
||||
{
|
||||
var r = SemanticVersionRange.Parse(range);
|
||||
|
||||
await Assert.That(r.Contains(SemanticVersion.Parse(inside))).IsTrue();
|
||||
await Assert.That(r.Contains(SemanticVersion.Parse(outside))).IsFalse();
|
||||
await Assert.That(r.ToString("n", null)).IsEqualTo(expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Arguments("[1.2.3]", "1.2.3", true)]
|
||||
[Arguments("[1.2.3]", "1.2.4", false)]
|
||||
|
|
|
|||
|
|
@ -78,6 +78,10 @@ public readonly partial record struct SemanticVersion : ISpanParsable<SemanticVe
|
|||
|
||||
#endregion
|
||||
|
||||
// components = 0: wildcard at major
|
||||
// components = 1: wildcard at minor
|
||||
// components = 2: wildcard at patch
|
||||
// components = 3: no wildcards
|
||||
internal static bool TryParsePartially(ReadOnlySpan<char> s, out SemanticVersion version, out int components)
|
||||
{
|
||||
version = default;
|
||||
|
|
|
|||
|
|
@ -106,92 +106,85 @@ public readonly partial record struct SemanticVersionRange : ISpanFormattable
|
|||
|
||||
#endregion
|
||||
|
||||
private static bool TryWriteComparator(ref SpanBuffer buf, Comparator c)
|
||||
private static bool TryFormatSimpleNpm(ref SpanBuffer buf, ComparatorGroup group)
|
||||
{
|
||||
ReadOnlySpan<char> op = c.Op switch
|
||||
if (group.Comparators is [])
|
||||
{
|
||||
ComparatorOp.Neq => "!=",
|
||||
ComparatorOp.Lt => "<",
|
||||
ComparatorOp.Lte => "<=",
|
||||
ComparatorOp.Gt => ">",
|
||||
ComparatorOp.Gte => ">=",
|
||||
_ => []
|
||||
};
|
||||
return buf.TryWrite('*');
|
||||
}
|
||||
|
||||
if (!op.IsEmpty && !buf.TryWrite(op))
|
||||
if (group.Comparators is [{ Op: ComparatorOp.Eq, Version: var version }])
|
||||
{
|
||||
return buf.TryWrite(version);
|
||||
}
|
||||
|
||||
if (group.Comparators.Length is not 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return buf.TryWrite(c.Version);
|
||||
}
|
||||
|
||||
private static bool TryFormatSimpleNpm(ref SpanBuffer buf, ComparatorGroup group)
|
||||
{
|
||||
var c = group.Comparators;
|
||||
var checkpoint = buf.Written;
|
||||
|
||||
if (c.Length is 0)
|
||||
if (group is { Lower: { Op: ComparatorOp.Gte or ComparatorOp.Gt, Version: var lo }, Upper: { Op: ComparatorOp.Lt or ComparatorOp.Lte, Version: var hi } upper })
|
||||
{
|
||||
return buf.TryWrite('*');
|
||||
}
|
||||
|
||||
if (c is [{ Op: ComparatorOp.Eq }])
|
||||
{
|
||||
return buf.TryWrite(c[0].Version) || buf.Reset(checkpoint);
|
||||
}
|
||||
|
||||
if (c.Length is not 2)
|
||||
{
|
||||
return buf.Reset(checkpoint);
|
||||
}
|
||||
|
||||
var lower = group.Lower!.Value;
|
||||
var upper = group.Upper!.Value;
|
||||
|
||||
if (lower.Op is ComparatorOp.Gte or ComparatorOp.Gt && upper.Op is ComparatorOp.Lt or ComparatorOp.Lte)
|
||||
{
|
||||
var lo = lower.Version;
|
||||
var hi = upper.Version;
|
||||
|
||||
if (upper.Op == ComparatorOp.Lt && hi is { Minor: 0, Patch: 0, Prerelease: not { Length: > 0 } })
|
||||
if (upper.Op is ComparatorOp.Lt && hi is { Minor: 0, Patch: 0, Prerelease: not { Length: > 0 } })
|
||||
{
|
||||
if ((lo.Major > 0 && hi.Major == lo.Major + 1) ||
|
||||
(lo is { Major: 0, Minor: > 0 } && hi.Major == 0 && hi.Minor == lo.Minor + 1) ||
|
||||
(lo is { Major: 0, Minor: 0 } && hi is { Major: 0, Minor: 0 } && hi.Patch == lo.Patch + 1))
|
||||
if (lo is { Major: > 0 } && hi.Major == lo.Major + 1)
|
||||
{
|
||||
return (buf.TryWrite('^') && buf.TryWrite(lo)) || buf.Reset(checkpoint);
|
||||
return buf.TryWrite('^') && buf.TryWrite(lo);
|
||||
}
|
||||
|
||||
if (lo is { Major: 0, Minor: > 0 } && hi is { Major: 0 } && hi.Minor == lo.Minor + 1)
|
||||
{
|
||||
return buf.TryWrite('^') && buf.TryWrite(lo);
|
||||
}
|
||||
|
||||
if (lo is { Major: 0, Minor: 0 } && hi is { Major: 0, Minor: 0 } && hi.Patch == lo.Patch + 1)
|
||||
{
|
||||
return buf.TryWrite('^') && buf.TryWrite(lo);
|
||||
}
|
||||
}
|
||||
|
||||
if (upper.Op == ComparatorOp.Lt &&
|
||||
hi is { Patch: 0, Prerelease: not { Length: > 0 } } &&
|
||||
hi.Major == lo.Major &&
|
||||
hi.Minor == lo.Minor + 1)
|
||||
if (upper.Op is ComparatorOp.Lt && hi is { Patch: 0, Prerelease: not { Length: > 0 } })
|
||||
{
|
||||
return (buf.TryWrite('~') && buf.TryWrite(lo)) || buf.Reset(checkpoint);
|
||||
if (hi.Major == lo.Major && hi.Minor == lo.Minor + 1)
|
||||
{
|
||||
return buf.TryWrite('~') && buf.TryWrite(lo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Reset(checkpoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryFormatNormalNpm(ref SpanBuffer buf, ComparatorGroup group)
|
||||
{
|
||||
var comps = group.Comparators;
|
||||
if (comps.Length is 0)
|
||||
if (group.Comparators.Length is 0)
|
||||
{
|
||||
return buf.TryWrite('*');
|
||||
}
|
||||
|
||||
for (var j = 0; j < comps.Length; j++)
|
||||
for (var i = 0; i < group.Comparators.Length; i++)
|
||||
{
|
||||
if (j > 0 && !buf.TryWrite(' '))
|
||||
if (i > 0 && !buf.TryWrite(' '))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryWriteComparator(ref buf, comps[j]))
|
||||
var @operator = group.Comparators[i].Op switch
|
||||
{
|
||||
ComparatorOp.Neq => "!=",
|
||||
ComparatorOp.Lt => "<",
|
||||
ComparatorOp.Lte => "<=",
|
||||
ComparatorOp.Gt => ">",
|
||||
ComparatorOp.Gte => ">=",
|
||||
_ => ReadOnlySpan<char>.Empty,
|
||||
};
|
||||
|
||||
if (!@operator.IsEmpty && !buf.TryWrite(@operator))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!buf.TryWrite(group.Comparators[i].Version))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -204,8 +197,7 @@ public readonly partial record struct SemanticVersionRange : ISpanFormattable
|
|||
{
|
||||
if (group.Comparators is [{ Op: ComparatorOp.Eq, Version: var version }])
|
||||
{
|
||||
var exactCheckpoint = buf.Written;
|
||||
return (buf.TryWrite('[') && buf.TryWrite(version) && buf.TryWrite(']')) || buf.Reset(exactCheckpoint);
|
||||
return buf.TryWrite('[') && buf.TryWrite(version) && buf.TryWrite(']');
|
||||
}
|
||||
|
||||
if (group.Comparators.Any(c => c.Op is ComparatorOp.Neq) || group.Comparators.Length > 2)
|
||||
|
|
@ -213,13 +205,10 @@ public readonly partial record struct SemanticVersionRange : ISpanFormattable
|
|||
return TryFormatNormalNpm(ref buf, group);
|
||||
}
|
||||
|
||||
var checkpoint = buf.Written;
|
||||
|
||||
return (buf.TryWrite(group.Lower?.Op == ComparatorOp.Gte ? '[' : '(') &&
|
||||
(!group.Lower.HasValue || buf.TryWrite(group.Lower.Value.Version)) &&
|
||||
buf.TryWrite(',') &&
|
||||
(!group.Upper.HasValue || buf.TryWrite(group.Upper.Value.Version)) &&
|
||||
buf.TryWrite(group.Upper?.Op == ComparatorOp.Lte ? ']' : ')')) ||
|
||||
buf.Reset(checkpoint);
|
||||
return buf.TryWrite(group.Lower?.Op == ComparatorOp.Gte ? '[' : '(') &&
|
||||
(!group.Lower.HasValue || buf.TryWrite(group.Lower.Value.Version)) &&
|
||||
buf.TryWrite(',') &&
|
||||
(!group.Upper.HasValue || buf.TryWrite(group.Upper.Value.Version)) &&
|
||||
buf.TryWrite(group.Upper?.Op == ComparatorOp.Lte ? ']' : ')');
|
||||
}
|
||||
}
|
||||
|
|
@ -145,6 +145,7 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
private static bool TryParseNpmGroup(ReadOnlySpan<char> s, out ComparatorGroup group)
|
||||
{
|
||||
group = default;
|
||||
|
||||
if (s.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -173,7 +174,7 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
var token = end >= 0 ? s[..end] : s;
|
||||
tokens++;
|
||||
|
||||
if (IsWildcardToken(token))
|
||||
if (token.ContainsAny("*xX"))
|
||||
{
|
||||
if (!TryExpandWildcardComparators(token, comparators))
|
||||
{
|
||||
|
|
@ -193,7 +194,7 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
s = end >= 0 ? s[(end + 1)..] : [];
|
||||
}
|
||||
|
||||
if (comparators.Count == 0 && tokens == 0)
|
||||
if (comparators.Count is 0 && tokens is 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -233,43 +234,32 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
private static bool TryParseCaretRange(ReadOnlySpan<char> s, out ComparatorGroup group)
|
||||
{
|
||||
group = default;
|
||||
|
||||
if (s.IsEmpty || s[0] is not '^')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
s = s[1..];
|
||||
// if (!TryParseWildcardVersion(s, out var version, out var minorWild, out var patchWild))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
if (!SemanticVersion.TryParsePartially(s, out var version, out var comp))
|
||||
|
||||
if (!SemanticVersion.TryParsePartially(s, out var lo, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var lo = comp is 0
|
||||
? new SemanticVersion(version.Major, 0, 0)
|
||||
: comp is 1
|
||||
? new SemanticVersion(version.Major, version.Minor, 0)
|
||||
: version;
|
||||
SemanticVersion hi;
|
||||
|
||||
if (version.Major > 0)
|
||||
if (lo.Major > 0)
|
||||
{
|
||||
hi = new SemanticVersion(version.Major + 1, 0, 0);
|
||||
hi = new SemanticVersion(lo.Major + 1, 0, 0);
|
||||
}
|
||||
else if (comp is not 1 && version.Minor > 0)
|
||||
else if (lo.Minor > 0)
|
||||
{
|
||||
hi = new SemanticVersion(0, version.Minor + 1, 0);
|
||||
}
|
||||
else if (comp is not 2 and not 1)
|
||||
{
|
||||
hi = new SemanticVersion(0, 0, version.Patch + 1);
|
||||
hi = new SemanticVersion(0, lo.Minor + 1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = new SemanticVersion(0, 1, 0);
|
||||
hi = new SemanticVersion(0, 0, lo.Patch + 1);
|
||||
}
|
||||
|
||||
group = new ComparatorGroup([new Comparator(ComparatorOp.Gte, lo), new Comparator(ComparatorOp.Lt, hi)]);
|
||||
|
|
@ -279,21 +269,21 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
private static bool TryParseTildeRange(ReadOnlySpan<char> s, out ComparatorGroup group)
|
||||
{
|
||||
group = default;
|
||||
|
||||
if (s.IsEmpty || s[0] is not '~')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
s = s[1..];
|
||||
if (!SemanticVersion.TryParsePartially(s, out var lo, out var comp))
|
||||
if (!SemanticVersion.TryParsePartially(s, out var lo, out var wildcard))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SemanticVersion hi;
|
||||
|
||||
if (comp is 1)
|
||||
if (wildcard is 1)
|
||||
{
|
||||
hi = new SemanticVersion(lo.Major + 1, 0, 0);
|
||||
}
|
||||
|
|
@ -366,26 +356,6 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool IsWildcardToken(ReadOnlySpan<char> s)
|
||||
{
|
||||
// Skip operator chars
|
||||
var i = 0;
|
||||
while (i < s.Length && (s[i] is '>' or '<' or '=' or '~' or '^'))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
for (; i < s.Length; i++)
|
||||
{
|
||||
if (s[i] is '*' or 'x' or 'X')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<char> StripOp(ReadOnlySpan<char> s, out ComparatorOp op)
|
||||
{
|
||||
op = ComparatorOp.Eq;
|
||||
|
|
@ -419,150 +389,41 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
return s[i..];
|
||||
}
|
||||
|
||||
private static bool TryExpandWildcardComparators(ReadOnlySpan<char> token, List<Comparator> comparators)
|
||||
private static bool TryExpandWildcardComparators(ReadOnlySpan<char> s, List<Comparator> comparators)
|
||||
{
|
||||
var s = StripOp(token.Trim(), out var op);
|
||||
s = StripOp(s.Trim(), out var op);
|
||||
|
||||
if (op is not ComparatorOp.Eq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryParseWildcardVersion(s, out var version, out var minorWild, out var patchWild))
|
||||
if (!SemanticVersion.TryParsePartially(s, out var lo, out var wildcard))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!minorWild && !patchWild)
|
||||
{
|
||||
comparators.Add(new Comparator(ComparatorOp.Eq, version));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s is ['*' or 'x' or 'X'])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
comparators.Add(new Comparator(ComparatorOp.Gte, new SemanticVersion(version.Major, version.Minor, version.Patch)));
|
||||
var upper = minorWild
|
||||
? new SemanticVersion(version.Major + 1, 0, 0)
|
||||
: new SemanticVersion(version.Major, version.Minor + 1, 0);
|
||||
comparators.Add(new Comparator(ComparatorOp.Lt, upper));
|
||||
SemanticVersion hi;
|
||||
|
||||
if (wildcard is 1)
|
||||
{
|
||||
hi = new SemanticVersion(lo.Major + 1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = new SemanticVersion(lo.Major, lo.Minor + 1, 0);
|
||||
}
|
||||
|
||||
comparators.Add(new Comparator(ComparatorOp.Gte, lo));
|
||||
comparators.Add(new Comparator(ComparatorOp.Lt, hi));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseWildcardVersion(ReadOnlySpan<char> s, out SemanticVersion version, out bool minorWild, out bool patchWild)
|
||||
{
|
||||
version = default;
|
||||
minorWild = patchWild = false;
|
||||
if (s.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle bare * or x/X
|
||||
if (s is ['*' or 'x' or 'X'])
|
||||
{
|
||||
minorWild = patchWild = true;
|
||||
version = new SemanticVersion(0, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
ulong major = 0, minor = 0, patch = 0;
|
||||
var pos = 0;
|
||||
if (!TryParseNonNegativeInt(s, ref pos, out major))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos >= s.Length)
|
||||
{
|
||||
minorWild = patchWild = true;
|
||||
version = new SemanticVersion(major, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s[pos] != '.')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pos++;
|
||||
|
||||
if (pos < s.Length && (s[pos] == '*' || s[pos] == 'x' || s[pos] == 'X'))
|
||||
{
|
||||
minorWild = patchWild = true;
|
||||
pos++;
|
||||
version = new SemanticVersion(major, 0, 0);
|
||||
return pos == s.Length;
|
||||
}
|
||||
|
||||
if (!TryParseNonNegativeInt(s, ref pos, out minor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos >= s.Length)
|
||||
{
|
||||
patchWild = true;
|
||||
version = new SemanticVersion(major, minor, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s[pos] != '.')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pos++;
|
||||
|
||||
if (pos < s.Length && (s[pos] == '*' || s[pos] == 'x' || s[pos] == 'X'))
|
||||
{
|
||||
patchWild = true;
|
||||
pos++;
|
||||
version = new SemanticVersion(major, minor, 0);
|
||||
return pos == s.Length;
|
||||
}
|
||||
|
||||
if (!TryParseNonNegativeInt(s, ref pos, out patch))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos == s.Length)
|
||||
{
|
||||
version = new SemanticVersion(major, minor, patch);
|
||||
return true;
|
||||
}
|
||||
|
||||
return (s[pos] == '-' || s[pos] == '+') && SemanticVersion.TryParse(s, null, out version);
|
||||
}
|
||||
|
||||
internal static bool TryParseNonNegativeInt(ReadOnlySpan<char> s, ref int pos, out ulong value)
|
||||
{
|
||||
value = 0;
|
||||
var start = pos;
|
||||
while (pos < s.Length && char.IsAsciiDigit(s[pos]))
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (pos == start)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos - start > 1 && s[start] == '0')
|
||||
{
|
||||
return false; // no leading zeros
|
||||
}
|
||||
|
||||
return ulong.TryParse(s[start..pos], out value);
|
||||
}
|
||||
|
||||
|
||||
// ── Maven parsing ────────────────────────────────────────────────────────
|
||||
|
||||
private static bool TryParseMaven(ReadOnlySpan<char> s, out SemanticVersionRange result)
|
||||
{
|
||||
result = default;
|
||||
|
|
@ -628,59 +489,58 @@ public readonly partial record struct SemanticVersionRange : ISpanParsable<Seman
|
|||
private static bool TryParseMavenGroup(ReadOnlySpan<char> s, out ComparatorGroup group)
|
||||
{
|
||||
group = default;
|
||||
var lInclusive = s[0] == '[';
|
||||
var rInclusive = s[^1] == ']';
|
||||
|
||||
var inner = s[1..^1];
|
||||
var commaIdx = inner.IndexOf(',');
|
||||
if (commaIdx < 0)
|
||||
var loInclusive = s[0] is '[';
|
||||
var hiInclusive = s[^1] is ']';
|
||||
|
||||
s = s[1..^1];
|
||||
|
||||
if (s.IndexOf(',') is not (>= 0 and var i))
|
||||
{
|
||||
if (!lInclusive || !rInclusive || !SemanticVersion.TryParsePartially(inner.Trim(), out var exact, out _))
|
||||
if (!loInclusive || !hiInclusive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
group = new ComparatorGroup([new Comparator(ComparatorOp.Eq, exact)]);
|
||||
if (!SemanticVersion.TryParsePartially(s.Trim(), out var version, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
group = new ComparatorGroup([new Comparator(ComparatorOp.Eq, version)]);
|
||||
return true;
|
||||
}
|
||||
|
||||
var loStr = inner[..commaIdx].Trim();
|
||||
var hiStr = inner[(commaIdx + 1)..].Trim();
|
||||
|
||||
var comps = new List<Comparator>();
|
||||
|
||||
if (!loStr.IsEmpty)
|
||||
if (s[..i].Trim() is { IsEmpty: false } loStr)
|
||||
{
|
||||
if (!SemanticVersion.TryParsePartially(loStr, out var lo, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
comps.Add(new Comparator(lInclusive ? ComparatorOp.Gte : ComparatorOp.Gt, lo));
|
||||
comps.Add(new Comparator(loInclusive ? ComparatorOp.Gte : ComparatorOp.Gt, lo));
|
||||
}
|
||||
|
||||
if (!hiStr.IsEmpty)
|
||||
if (s[(i + 1)..].Trim() is { IsEmpty: false } hiStr)
|
||||
{
|
||||
if (!SemanticVersion.TryParsePartially(hiStr, out var hi, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
comps.Add(new Comparator(rInclusive ? ComparatorOp.Lte : ComparatorOp.Lt, hi));
|
||||
}
|
||||
|
||||
if (comps.Count == 0)
|
||||
{
|
||||
group = new ComparatorGroup([]);
|
||||
return true;
|
||||
comps.Add(new Comparator(hiInclusive ? ComparatorOp.Lte : ComparatorOp.Lt, hi));
|
||||
}
|
||||
|
||||
// Check for exact match [a,a]
|
||||
if (comps is [{ Op: ComparatorOp.Gte }, { Op: ComparatorOp.Lte }] &&
|
||||
comps[0].Version == comps[1].Version)
|
||||
if (comps is [{ Op: ComparatorOp.Gte, Version: var lhs }, { Op: ComparatorOp.Lte, Version: var rhs }])
|
||||
{
|
||||
group = new ComparatorGroup([new Comparator(ComparatorOp.Eq, comps[0].Version)]);
|
||||
return true;
|
||||
if (lhs == rhs)
|
||||
{
|
||||
group = new ComparatorGroup([new Comparator(ComparatorOp.Eq, lhs)]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
group = new ComparatorGroup([.. comps]);
|
||||
|
|
|
|||
|
|
@ -40,11 +40,4 @@ internal ref struct SpanBuffer(Span<char> buffer)
|
|||
Written += n;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resets position to a saved checkpoint and returns false — useful in || chains.
|
||||
public bool Reset(int checkpoint)
|
||||
{
|
||||
Written = checkpoint;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue