using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text.Json.Serialization;
namespace StellaOps.Feedser.Models;
///
/// Canonical affected package descriptor with deterministic ordering of ranges and provenance.
///
public sealed record AffectedPackage
{
public static AffectedPackage Empty { get; } = new(
AffectedPackageTypes.SemVer,
identifier: "unknown",
platform: null,
versionRanges: Array.Empty(),
statuses: Array.Empty(),
provenance: Array.Empty());
[JsonConstructor]
public AffectedPackage(
string type,
string identifier,
string? platform = null,
IEnumerable? versionRanges = null,
IEnumerable? statuses = null,
IEnumerable? provenance = null)
{
Type = Validation.EnsureNotNullOrWhiteSpace(type, nameof(type)).ToLowerInvariant();
Identifier = Validation.EnsureNotNullOrWhiteSpace(identifier, nameof(identifier));
Platform = Validation.TrimToNull(platform);
VersionRanges = (versionRanges ?? Array.Empty())
.Distinct(AffectedVersionRangeEqualityComparer.Instance)
.OrderBy(static range => range, AffectedVersionRangeComparer.Instance)
.ToImmutableArray();
Statuses = (statuses ?? Array.Empty())
.Where(static status => status is not null)
.Distinct(AffectedPackageStatusEqualityComparer.Instance)
.OrderBy(static status => status.Status, StringComparer.Ordinal)
.ThenBy(static status => status.Provenance.Source, StringComparer.Ordinal)
.ThenBy(static status => status.Provenance.Kind, StringComparer.Ordinal)
.ThenBy(static status => status.Provenance.RecordedAt)
.ToImmutableArray();
Provenance = (provenance ?? Array.Empty())
.Where(static p => p is not null)
.OrderBy(static p => p.Source, StringComparer.Ordinal)
.ThenBy(static p => p.Kind, StringComparer.Ordinal)
.ThenBy(static p => p.RecordedAt)
.ToImmutableArray();
}
///
/// Semantic type of the coordinates (rpm, deb, cpe, semver, vendor, ics-vendor).
///
public string Type { get; }
///
/// Canonical identifier for the package (NEVRA, PackageURL, CPE string, vendor slug, etc.).
///
public string Identifier { get; }
public string? Platform { get; }
public ImmutableArray VersionRanges { get; }
public ImmutableArray Statuses { get; }
public ImmutableArray Provenance { get; }
}
///
/// Known values for .
///
public static class AffectedPackageTypes
{
public const string Rpm = "rpm";
public const string Deb = "deb";
public const string Cpe = "cpe";
public const string SemVer = "semver";
public const string Vendor = "vendor";
public const string IcsVendor = "ics-vendor";
}