Загрузка данных


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

public class ServicePackageInfo
{
    public string FileName { get; set; } = string.Empty;

    public string ServiceName { get; set; } = string.Empty;

    public string Version { get; set; } = string.Empty;

    public int? AlphaVersion { get; set; }

    public int? BuildVersion { get; set; }

    public string? Release { get; set; }

    public string Architecture { get; set; } = string.Empty;

    public override string ToString()
    {
        return
            $"FileName: {FileName}, " +
            $"ServiceName: {ServiceName}, " +
            $"Version: {Version}, " +
            $"AlphaVersion: {AlphaVersion?.ToString() ?? "-"}, " +
            $"BuildVersion: {BuildVersion?.ToString() ?? "-"}, " +
            $"Release: {Release ?? "-"}, " +
            $"Architecture: {Architecture}";
    }
}

public static class RpmServiceParser
{
    private static readonly Regex RpmRegex = new Regex(
        @"^(?<name>.+)-(?<version>\d+(?:\.\d+)*)-(?<release>(?<releaseType>alpha|build)(?<releaseNumber>\d+)|alt\d+)\.(?<arch>[^.]+)\.rpm$",
        RegexOptions.Compiled | RegexOptions.IgnoreCase
    );

    public static ServicePackageInfo? Parse(string lineOrFileName)
    {
        if (string.IsNullOrWhiteSpace(lineOrFileName))
            return null;

        string fileName = ExtractFileName(lineOrFileName);

        Match match = RpmRegex.Match(fileName);

        if (!match.Success)
            return null;

        string rawServiceName = match.Groups["name"].Value;
        string version = match.Groups["version"].Value;
        string release = match.Groups["release"].Value;
        string releaseType = match.Groups["releaseType"].Value;
        string releaseNumber = match.Groups["releaseNumber"].Value;
        string architecture = match.Groups["arch"].Value;

        string serviceName = NormalizeServiceName(rawServiceName);

        var result = new ServicePackageInfo
        {
            FileName = fileName,
            ServiceName = serviceName,
            Version = version,
            Release = release,
            Architecture = architecture
        };

        if (releaseType.Equals("alpha", StringComparison.OrdinalIgnoreCase))
            result.AlphaVersion = int.Parse(releaseNumber);

        if (releaseType.Equals("build", StringComparison.OrdinalIgnoreCase))
            result.BuildVersion = int.Parse(releaseNumber);

        return result;
    }

    public static List<ServicePackageInfo> ParseMany(IEnumerable<string> lines)
    {
        return lines
            .Select(Parse)
            .Where(x => x != null)
            .Cast<ServicePackageInfo>()
            .ToList();
    }

    private static string ExtractFileName(string lineOrFileName)
    {
        string value = lineOrFileName.Trim();

        // Если передали строку из листинга:
        // eas4-cart-8.3.0-build15040.x86_64.rpm 18-Jun-2026 13:56 45965188
        value = value.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0];

        // Если передали ссылку:
        // https://site.ru/repo/eas4-cart-8.3.0-build15040.x86_64.rpm
        int queryIndex = value.IndexOf('?');
        if (queryIndex >= 0)
            value = value.Substring(0, queryIndex);

        int lastSlashIndex = value.LastIndexOf('/');
        if (lastSlashIndex >= 0)
            value = value.Substring(lastSlashIndex + 1);

        return value;
    }

    private static string NormalizeServiceName(string rawServiceName)
    {
        var ignoredParts = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            "backend",
            "debuginfo"
        };

        return string.Join(
            "-",
            rawServiceName
                .Split('-', StringSplitOptions.RemoveEmptyEntries)
                .Where(part => !ignoredParts.Contains(part))
        );
    }
}