using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; using StabilityMatrix.Core.Processes; namespace StabilityMatrix.Core.Python; public partial record PipPackageSpecifier { public required string Name { get; init; } public string? Constraint { get; init; } public string? Version { get; init; } public string? VersionConstraint => Constraint is null || Version is null ? null : Constraint + Name; public static PipPackageSpecifier Parse(string value) { var result = TryParse(value, true, out var packageSpecifier); Debug.Assert(result); return packageSpecifier!; } public static bool TryParse(string value, [NotNullWhen(true)] out PipPackageSpecifier? packageSpecifier) { return TryParse(value, false, out packageSpecifier); } private static bool TryParse( string value, bool throwOnFailure, [NotNullWhen(true)] out PipPackageSpecifier? packageSpecifier ) { var match = PackageSpecifierRegex().Match(value); if (!match.Success) { if (throwOnFailure) { throw new ArgumentException($"Invalid package specifier: {value}"); } packageSpecifier = null; return false; } packageSpecifier = new PipPackageSpecifier { Name = match.Groups["package_name"].Value, Constraint = match.Groups["version_constraint"].Value, Version = match.Groups["version"].Value }; return true; } /// public override string ToString() { return Name + VersionConstraint; } public static implicit operator Argument(PipPackageSpecifier specifier) { return specifier.VersionConstraint is null ? new Argument(specifier.Name) : new Argument((specifier.Name, specifier.VersionConstraint)); } public static implicit operator PipPackageSpecifier(string specifier) { return Parse(specifier); } /// /// Regex to match a pip package specifier. /// [GeneratedRegex( "(?[a-zA-Z0-9_]+)(?(?==|>=|<=|>|<|~=|!=)([a-zA-Z0-9_.]+))?", RegexOptions.CultureInvariant, 1000 )] private static partial Regex PackageSpecifierRegex(); }