265 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
namespace StellaOps.Concelier.Merge.Comparers;
 | 
						|
 | 
						|
using System;
 | 
						|
using StellaOps.Concelier.Normalization.Distro;
 | 
						|
 | 
						|
public sealed class NevraComparer : IComparer<Nevra>, IComparer<string>
 | 
						|
{
 | 
						|
    public static NevraComparer Instance { get; } = new();
 | 
						|
 | 
						|
    private NevraComparer()
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    public int Compare(string? x, string? y)
 | 
						|
    {
 | 
						|
        if (ReferenceEquals(x, y))
 | 
						|
        {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        if (x is null)
 | 
						|
        {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        if (y is null)
 | 
						|
        {
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
 | 
						|
        var xParsed = Nevra.TryParse(x, out var xNevra);
 | 
						|
        var yParsed = Nevra.TryParse(y, out var yNevra);
 | 
						|
 | 
						|
        if (xParsed && yParsed)
 | 
						|
        {
 | 
						|
            return Compare(xNevra, yNevra);
 | 
						|
        }
 | 
						|
 | 
						|
        if (xParsed)
 | 
						|
        {
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
 | 
						|
        if (yParsed)
 | 
						|
        {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        return string.Compare(x, y, StringComparison.Ordinal);
 | 
						|
    }
 | 
						|
 | 
						|
    public int Compare(Nevra? x, Nevra? y)
 | 
						|
    {
 | 
						|
        if (ReferenceEquals(x, y))
 | 
						|
        {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        if (x is null)
 | 
						|
        {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        if (y is null)
 | 
						|
        {
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
 | 
						|
        var compare = string.Compare(x.Name, y.Name, StringComparison.Ordinal);
 | 
						|
        if (compare != 0)
 | 
						|
        {
 | 
						|
            return compare;
 | 
						|
        }
 | 
						|
 | 
						|
        compare = string.Compare(x.Architecture ?? string.Empty, y.Architecture ?? string.Empty, StringComparison.Ordinal);
 | 
						|
        if (compare != 0)
 | 
						|
        {
 | 
						|
            return compare;
 | 
						|
        }
 | 
						|
 | 
						|
        compare = x.Epoch.CompareTo(y.Epoch);
 | 
						|
        if (compare != 0)
 | 
						|
        {
 | 
						|
            return compare;
 | 
						|
        }
 | 
						|
 | 
						|
        compare = RpmVersionComparer.Compare(x.Version, y.Version);
 | 
						|
        if (compare != 0)
 | 
						|
        {
 | 
						|
            return compare;
 | 
						|
        }
 | 
						|
 | 
						|
        compare = RpmVersionComparer.Compare(x.Release, y.Release);
 | 
						|
        if (compare != 0)
 | 
						|
        {
 | 
						|
            return compare;
 | 
						|
        }
 | 
						|
 | 
						|
        return string.Compare(x.Original, y.Original, StringComparison.Ordinal);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
internal static class RpmVersionComparer
 | 
						|
{
 | 
						|
    public static int Compare(string? left, string? right)
 | 
						|
    {
 | 
						|
        left ??= string.Empty;
 | 
						|
        right ??= string.Empty;
 | 
						|
 | 
						|
        var i = 0;
 | 
						|
        var j = 0;
 | 
						|
 | 
						|
        while (true)
 | 
						|
        {
 | 
						|
            var leftHasTilde = SkipToNextSegment(left, ref i);
 | 
						|
            var rightHasTilde = SkipToNextSegment(right, ref j);
 | 
						|
 | 
						|
            if (leftHasTilde || rightHasTilde)
 | 
						|
            {
 | 
						|
                if (leftHasTilde && rightHasTilde)
 | 
						|
                {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                return leftHasTilde ? -1 : 1;
 | 
						|
            }
 | 
						|
 | 
						|
            var leftEnd = i >= left.Length;
 | 
						|
            var rightEnd = j >= right.Length;
 | 
						|
            if (leftEnd || rightEnd)
 | 
						|
            {
 | 
						|
                if (leftEnd && rightEnd)
 | 
						|
                {
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
 | 
						|
                return leftEnd ? -1 : 1;
 | 
						|
            }
 | 
						|
 | 
						|
            var leftDigit = char.IsDigit(left[i]);
 | 
						|
            var rightDigit = char.IsDigit(right[j]);
 | 
						|
 | 
						|
            if (leftDigit && !rightDigit)
 | 
						|
            {
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
 | 
						|
            if (!leftDigit && rightDigit)
 | 
						|
            {
 | 
						|
                return -1;
 | 
						|
            }
 | 
						|
 | 
						|
            int compare;
 | 
						|
            if (leftDigit)
 | 
						|
            {
 | 
						|
                compare = CompareNumericSegment(left, ref i, right, ref j);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                compare = CompareAlphaSegment(left, ref i, right, ref j);
 | 
						|
            }
 | 
						|
 | 
						|
            if (compare != 0)
 | 
						|
            {
 | 
						|
                return compare;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private static bool SkipToNextSegment(string value, ref int index)
 | 
						|
    {
 | 
						|
        var sawTilde = false;
 | 
						|
        while (index < value.Length)
 | 
						|
        {
 | 
						|
            var current = value[index];
 | 
						|
            if (current == '~')
 | 
						|
            {
 | 
						|
                sawTilde = true;
 | 
						|
                index++;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            if (char.IsLetterOrDigit(current))
 | 
						|
            {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            index++;
 | 
						|
        }
 | 
						|
 | 
						|
        return sawTilde;
 | 
						|
    }
 | 
						|
 | 
						|
    private static int CompareNumericSegment(string value, ref int index, string other, ref int otherIndex)
 | 
						|
    {
 | 
						|
        var start = index;
 | 
						|
        while (index < value.Length && char.IsDigit(value[index]))
 | 
						|
        {
 | 
						|
            index++;
 | 
						|
        }
 | 
						|
 | 
						|
        var otherStart = otherIndex;
 | 
						|
        while (otherIndex < other.Length && char.IsDigit(other[otherIndex]))
 | 
						|
        {
 | 
						|
            otherIndex++;
 | 
						|
        }
 | 
						|
 | 
						|
        var trimmedStart = start;
 | 
						|
        while (trimmedStart < index && value[trimmedStart] == '0')
 | 
						|
        {
 | 
						|
            trimmedStart++;
 | 
						|
        }
 | 
						|
 | 
						|
        var otherTrimmedStart = otherStart;
 | 
						|
        while (otherTrimmedStart < otherIndex && other[otherTrimmedStart] == '0')
 | 
						|
        {
 | 
						|
            otherTrimmedStart++;
 | 
						|
        }
 | 
						|
 | 
						|
        var length = index - trimmedStart;
 | 
						|
        var otherLength = otherIndex - otherTrimmedStart;
 | 
						|
 | 
						|
        if (length != otherLength)
 | 
						|
        {
 | 
						|
            return length.CompareTo(otherLength);
 | 
						|
        }
 | 
						|
 | 
						|
        var comparison = value.AsSpan(trimmedStart, length)
 | 
						|
            .CompareTo(other.AsSpan(otherTrimmedStart, otherLength), StringComparison.Ordinal);
 | 
						|
        if (comparison != 0)
 | 
						|
        {
 | 
						|
            return comparison;
 | 
						|
        }
 | 
						|
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    private static int CompareAlphaSegment(string value, ref int index, string other, ref int otherIndex)
 | 
						|
    {
 | 
						|
        var start = index;
 | 
						|
        while (index < value.Length && char.IsLetter(value[index]))
 | 
						|
        {
 | 
						|
            index++;
 | 
						|
        }
 | 
						|
 | 
						|
        var otherStart = otherIndex;
 | 
						|
        while (otherIndex < other.Length && char.IsLetter(other[otherIndex]))
 | 
						|
        {
 | 
						|
            otherIndex++;
 | 
						|
        }
 | 
						|
 | 
						|
        var length = index - start;
 | 
						|
        var otherLength = otherIndex - otherStart;
 | 
						|
 | 
						|
        var comparison = value.AsSpan(start, length)
 | 
						|
            .CompareTo(other.AsSpan(otherStart, otherLength), StringComparison.Ordinal);
 | 
						|
        if (comparison != 0)
 | 
						|
        {
 | 
						|
            return comparison;
 | 
						|
        }
 | 
						|
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
}
 |