using IPA.Config.Data; using IPA.Config.Stores; using IPA.Config.Stores.Converters; using System; using System.Collections.Generic; using Version = SemVer.Version; namespace IPA.Utilities { /// /// A type that wraps so that the string of the version is stored when the string is /// not a valid . /// public class AlmostVersion : IComparable, IComparable { /// /// Represents a storage type of either parsed object or raw . /// public enum StoredAs { /// /// The version was stored as a . /// SemVer, /// /// The version was stored as a . /// String } /// /// Creates a new with the version string provided in . /// /// the version string to store public AlmostVersion(string vertext) { if (!TryParseFrom(vertext, StoredAs.SemVer)) TryParseFrom(vertext, StoredAs.String); } /// /// Creates an from the provided in . /// /// the to store public AlmostVersion(Version ver) { SemverValue = ver; StorageMode = StoredAs.SemVer; } /// /// Creates an from the version string in stored using /// the storage mode specified in . /// /// the text to parse as an /// the storage mode to store the version in public AlmostVersion(string vertext, StoredAs mode) { if (!TryParseFrom(vertext, mode)) throw new ArgumentException($"{nameof(vertext)} could not be stored as {mode}!"); } /// /// Creates a new from the version string in stored the /// same way as the passed in . /// /// the text to parse as an /// an to copy the storage mode of public AlmostVersion(string vertext, AlmostVersion copyMode) { if (copyMode == null) throw new ArgumentNullException(nameof(copyMode)); if (!TryParseFrom(vertext, copyMode.StorageMode)) TryParseFrom(vertext, StoredAs.String); // silently parse differently } private bool TryParseFrom(string str, StoredAs mode) { if (mode == StoredAs.SemVer) try { SemverValue = new Version(str, true); StorageMode = StoredAs.SemVer; return true; } catch { return false; } else { StringValue = str; StorageMode = StoredAs.String; return true; } } /// /// The value of the if it was stored as a . /// /// the stored value as a , or if not stored as a string. public string StringValue { get; private set; } = null; /// /// The value of the if it was stored as a . /// /// the stored value as a , or if not stored as a version. public Version SemverValue { get; private set; } = null; /// /// The way the value is stored, whether it be as a or a . /// /// the storage mode used to store this value public StoredAs StorageMode { get; private set; } // can I just this? /// /// Gets a string representation of the current version. If the value is stored as a string, this returns it. If it is /// stored as a , it is equivalent to calling . /// /// a string representation of the current version /// public override string ToString() => StorageMode == StoredAs.SemVer ? SemverValue.ToString() : StringValue; /// /// Compares to the in using /// or , depending on the current store. /// /// /// The storage methods of the two objects must be the same, or this will throw an . /// /// the to compare to /// less than 0 if is considered bigger than , 0 if equal, and greater than zero if smaller /// public int CompareTo(AlmostVersion other) { if (other == null) return -1; if (StorageMode != other.StorageMode) throw new InvalidOperationException("Cannot compare AlmostVersions with different stores!"); if (StorageMode == StoredAs.SemVer) return SemverValue.CompareTo(other.SemverValue); else return StringValue.CompareTo(other.StringValue); } /// /// Compares to the in using . /// /// /// The storage method of must be , else an will /// be thrown. /// /// the to compare to /// less than 0 if is considered bigger than , 0 if equal, and greater than zero if smaller /// public int CompareTo(Version other) { if (StorageMode != StoredAs.SemVer) throw new InvalidOperationException("Cannot compare a SemVer version with an AlmostVersion stored as a string!"); return SemverValue.CompareTo(other); } /// /// Performs a strict equality check between and . /// /// /// This may return where returns /// /// the object to compare to /// if they are equal, otherwise /// public override bool Equals(object obj) { return obj is AlmostVersion version && SemverValue == version.SemverValue && StringValue == version.StringValue && StorageMode == version.StorageMode; } /// /// Default generated hash code function generated by VS. /// /// a value unique to each object, except those that are considered equal by /// public override int GetHashCode() { var hashCode = -126402897; hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(SemverValue); hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StringValue); hashCode = hashCode * -1521134295 + StorageMode.GetHashCode(); return hashCode; } /// /// Compares two versions, only taking into account the numeric part of the version if they are stored as s, /// or strict equality if they are stored as s. /// /// /// This is a looser equality than , meaning that this may return where /// does not. /// /// the first value to compare /// the second value to compare /// if they are mostly equal, otherwise /// public static bool operator==(AlmostVersion l, AlmostVersion r) { if (l is null && r is null) return true; if (l is null || r is null) return false; if (l.StorageMode != r.StorageMode) return false; if (l.StorageMode == StoredAs.SemVer) return Utils.VersionCompareNoPrerelease(l.SemverValue, r.SemverValue) == 0; else return l.StringValue == r.StringValue; } /// /// The opposite of . Equivalent to !(l == r). /// /// the first value to compare /// the second value to compare /// if they are not mostly equal, otherwise /// public static bool operator!=(AlmostVersion l, AlmostVersion r) => !(l == r); // implicitly convertible from Version /// /// Implicitly converts a to using . /// /// the to convert /// public static implicit operator AlmostVersion(Version ver) => new AlmostVersion(ver); // implicitly convertible to Version /// /// Implicitly converts an to , if applicable, using . /// If not applicable, returns /// /// the to convert to a /// public static implicit operator Version(AlmostVersion av) => av?.SemverValue; } /// /// A for s. /// public sealed class AlmostVersionConverter : ValueConverter { /// /// Converts a node into an . /// /// the node to convert /// the owner of the new object /// public override AlmostVersion FromValue(Value value, object parent) => new AlmostVersion(Converter.Default.FromValue(value, parent)); /// /// Converts an to a node. /// /// the to convert /// the parent of /// a node representing public override Value ToValue(AlmostVersion obj, object parent) => Value.From(obj.ToString()); } }