diff --git a/ZuneModCore/ReleaseVersion.cs b/ZuneModCore/ReleaseVersion.cs new file mode 100644 index 0000000..ff78ee4 --- /dev/null +++ b/ZuneModCore/ReleaseVersion.cs @@ -0,0 +1,483 @@ +using CommunityToolkit.Diagnostics; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace ZuneModCore; + +public sealed class ReleaseVersion : ICloneable, IComparable, IComparable, IEquatable +{ + /// + /// Gets the value of the major component of the version number for the current object. + /// + public int Major { get; set; } + + /// + /// Gets the value of the minor component of the version number for the current object. + /// + public int Minor { get; set; } + + /// + /// Gets the value of the build component of the version number for the current object. + /// + public int Build { get; set; } + + /// + /// Gets the value of the revision component of the version number for the current object. + /// + public int Revision { get; set; } + + private Phase _Phase = Phase.Unknown; + /// + /// Gets the value of the release phase component of the version number for the current object. + /// + public Phase Phase + { + set => _Phase = value; + get + { + if (_Phase == Phase.Unknown) + { +#if DEBUG + _Phase = Phase.Debug; +#else + _Phase = Phase.Production; +#endif + } + return _Phase; + } + } + + /// + /// Initializes a new instance of the class. + /// + public ReleaseVersion() + { + } + + /// + /// Initializes a new instance of the class with the specified major, + /// minor, build, revision, and release phase. + /// + /// + /// The major version number. + /// + /// + /// The minor version number. + /// + /// + /// The build version number. + /// + /// + /// The revision version number. + /// + /// + /// The release phase. + /// + /// + /// , , , or is less than zero. + /// + public ReleaseVersion(int major, int minor, int build = 0, int revision = 0, Phase phase = Phase.Unknown) + { + Guard.IsGreaterThanOrEqualTo(major, 0, nameof(major)); + Guard.IsGreaterThanOrEqualTo(minor, 0, nameof(minor)); + Guard.IsGreaterThanOrEqualTo(build, 0, nameof(build)); + Guard.IsGreaterThanOrEqualTo(revision, 0, nameof(revision)); + + Major = major; + Minor = minor; + Build = build; + Revision = revision; + Phase = phase; + } + + /// + /// Returns a new object whose value is the same as the current + /// object. + /// + /// + /// A new whose values are a copy of the current object. + /// + public object Clone() + { + return new ReleaseVersion(Major, Minor, Build, Revision, Phase); + } + + /// + /// Compares the current object to a specified object and returns + /// an indication of their relative values. + /// + /// + /// An object to compare, or null. + /// + /// + /// A signed integer that indicates the relative values of the two objects, as shown + /// in the following table. + /// Return value – Meaning + /// Less than zero – The current object is a version before . + /// Zero – The current object is the same version as . + /// Greater than zero – The current object is a version subsequent + /// to , or is null. + /// + /// + /// is not of type . + /// + public int CompareTo(object? version) + { + if (version == null) + { + return 1; + } + + if (version is ReleaseVersion v) + { + return CompareTo(v); + } + + throw new ArgumentException(nameof(version) + " must be of type " + nameof(ReleaseVersion)); + } + + /// + /// Compares the current object to a specified object + /// and returns an indication of their relative values. + /// + /// + /// A object to compare to the current object, or null. + /// + /// + /// A signed integer that indicates the relative values of the two objects, as shown + /// in the following table. + /// Return value – Meaning + /// Less than zero – The current object is a version before . + /// Zero – The current object is the same version as . + /// Greater than zero – The current object is a version subsequent + /// to , or is null. + /// + public int CompareTo(ReleaseVersion? value) + { + return + ReferenceEquals(value, this) ? 0 : + value is null ? 1 : + Major != value.Major ? (Major > value.Major ? 1 : -1) : + Minor != value.Minor ? (Minor > value.Minor ? 1 : -1) : + Build != value.Build ? (Build > value.Build ? 1 : -1) : + Revision != value.Revision ? (Revision > value.Revision ? 1 : -1) : + Phase != value.Phase? ((byte)Phase > (byte)value.Phase ? 1 : -1) : + 0; + } + + /// + /// Returns a value indicating whether the current object is equal + /// to a specified object. + /// + /// + /// An object to compare with the current object, or null. + /// + /// + /// true if the current object and + /// are both objects, + /// and every component of the current object matches the corresponding + /// component of ; otherwise, false. + /// + public override bool Equals(object? obj) + { + return Equals(obj as ReleaseVersion); + } + + /// + /// Returns a value indicating whether the current object and a specified + /// object represent the same value. + /// + /// + /// A object to compare to the current object, or null. + /// + /// + /// true if every component of the current object matches the corresponding + /// component of the parameter; otherwise, false. + /// + public bool Equals(ReleaseVersion? obj) + { + return ReferenceEquals(obj, this) || + (obj is not null && + Major == obj.Major && + Minor == obj.Minor && + Build == obj.Build && + Revision == obj.Revision && + Phase == obj.Phase); + } + + /// + /// Returns a hash code for the current object. + /// + /// + /// A 32-bit signed integer hash code. + /// + public override int GetHashCode() + { + return HashCode.Combine(Major, Minor, Build, Revision, Phase); + } + + /// + /// Determines whether two specified objects are equal. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// true if v1 equals v2; otherwise, false. + /// + // Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(ReleaseVersion? v1, ReleaseVersion? v2) + { + // Test "right" first to allow branch elimination when inlined for null checks (== null) + // so it can become a simple test + if (v2 is null) + { + // return true/false not the test result https://github.com/dotnet/runtime/issues/4207 + return (v1 is null) ? true : false; + } + + // Quick reference equality test prior to calling the virtual Equality + return ReferenceEquals(v2, v1) ? true : v2.Equals(v1); + } + + /// + /// Determines whether the first specified object is greater than + /// the second specified object. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// true if v1 is greater than v2; otherwise, false. + /// + public static bool operator >(ReleaseVersion? v1, ReleaseVersion? v2) + => v2 < v1; + + /// + /// Determines whether the first specified object is greater than + /// or equal to the second specified object. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// true if v1 is greater than or equal to v2; otherwise, false. + /// + public static bool operator >=(ReleaseVersion? v1, ReleaseVersion? v2) + => v2 <= v1; + + /// + /// Determines whether two specified objects are not equal. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// true if v1 does not equal v2; otherwise, false. + /// + public static bool operator !=(ReleaseVersion? v1, ReleaseVersion? v2) + => !(v1 == v2); + + /// + /// Determines whether the first specified object is less than the + /// second specified object. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// true if v1 is less than v2; otherwise, false. + /// + public static bool operator <(ReleaseVersion? v1, ReleaseVersion? v2) + { + if (v1 is null) + { + return !(v2 is null); + } + + return v1.CompareTo(v2) < 0; + } + + /// + /// Determines whether the first specified object is less than or + /// equal to the second object. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// true if v1 is less than or equal to v2; otherwise, false. + /// + public static bool operator <=(ReleaseVersion? v1, ReleaseVersion? v2) + { + if (v1 is null) + { + return true; + } + + return v1.CompareTo(v2) <= 0; + } + + /// + /// Converts the string representation of a version number to an equivalent + /// object. + /// + /// + /// A string containing the major, minor, build, and revision numbers, where each + /// number is delimited with a period character ('.'), followed by a dash character + /// ('-') and the release phase. + /// + /// + /// An object that is equivalent to the version number specified in the input parameter. + /// + public static ReleaseVersion Parse(string input) + { + ReleaseVersion version = new(); + string versionPart = input; + int phaseIdx = input.IndexOf('-'); + if (phaseIdx != -1) + { + versionPart = input[..phaseIdx]; + + string phaseComp = input[(phaseIdx + 1)..]; + if (phaseComp.ToLowerInvariant() == "rc") + version.Phase = Phase.ReleaseCandidate; + else + version.Phase = Enum.Parse(phaseComp, true); + } + + string[] components = versionPart.Split('.'); + Guard.IsGreaterThanOrEqualTo(components.Length, 2, nameof(components)); + + if (components.Length >= 2) + { + version.Major = int.Parse(components[0]); + version.Minor = int.Parse(components[1]); + + + if (components.Length >= 3) + { + version.Build = int.Parse(components[2]); + + + if (components.Length >= 4) + { + version.Revision = int.Parse(components[3]); + } + } + } + + return version; + } + + /// + /// Converts the value of the current object to its equivalent + /// representation. + /// + /// + /// The string representation of the values of the major, minor, build, and + /// revision components of the current object, as depicted in the + /// following format. Each component is separated by a period character ('.'), + /// and the release phase is appended to the end after a dash character ('-'). Square + /// brackets ('[' and ']') indicate a component that will not appear in the return + /// value if the component is not defined: major.minor[.build[.revision]][-phase] For example, + /// if you create a object using the constructor Version(1, 1), the + /// returned string is "1.1". If you create a object using the constructor + /// Version(1, 3, 4, 2, Phase.Beta), the returned string is "1.3.4.2-beta". + /// + public override string ToString() => ToString(true); + + /// + /// + /// When set to true, the string will be formatted as major.minor.build.revision-phase, + /// even if an optional component is not present. + /// + public string ToString(bool includeAllComponents) + { + const char numDelim = '.'; + const char phaseDelim = '-'; + string output = Major.ToString() + numDelim + Minor.ToString(); + + if (Build > 0 || includeAllComponents) + { + output += numDelim + Build.ToString(); + + if (Revision > 0 || includeAllComponents) + { + output += numDelim + Revision.ToString(); + } + } + + if ((Phase != Phase.Unknown && Phase != Phase.Production) || includeAllComponents) + { + output += phaseDelim + (Phase == Phase.ReleaseCandidate ? "rc" : Phase.ToString().ToLowerInvariant()); + } + + return output; + } + + /// + /// Tries to convert the string representation of a version number to an equivalent + /// object, and returns a value that indicates whether the conversion + /// succeeded. + /// + /// + /// A string that contains a version number to convert. + /// + /// + /// When this method returns, contains the equivalent of the number + /// that is contained in input, if the conversion succeeded. If input is null, , + /// or if the conversion fails, result is null when the method returns. + /// + /// + /// true if the input parameter was converted successfully; otherwise, false. + /// + public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out ReleaseVersion? result) + { + try + { + Guard.IsNotNull(input, nameof(input)); + result = Parse(input); + return true; + } + catch + { + result = null; + return false; + } + } +} + +public enum Phase : byte +{ + Unknown, + Debug, + Alpha, + Beta, + ReleaseCandidate, + Production +} diff --git a/ZuneModdingHelper/App.xaml.cs b/ZuneModdingHelper/App.xaml.cs index 68f9d78..03a72e6 100644 --- a/ZuneModdingHelper/App.xaml.cs +++ b/ZuneModdingHelper/App.xaml.cs @@ -1,6 +1,7 @@ using System.Windows; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.Extensions.DependencyInjection; +using ZuneModCore; using ZuneModCore.Services; using ZuneModdingHelper.Services; diff --git a/ZuneModdingHelper/Pages/AboutPage.xaml.cs b/ZuneModdingHelper/Pages/AboutPage.xaml.cs index a436e37..ff64ab7 100644 --- a/ZuneModdingHelper/Pages/AboutPage.xaml.cs +++ b/ZuneModdingHelper/Pages/AboutPage.xaml.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Navigation; +using ZuneModCore; using ZuneModdingHelper.Messages; namespace ZuneModdingHelper.Pages diff --git a/ZuneModdingHelper/ReleaseVersion.cs b/ZuneModdingHelper/ReleaseVersion.cs deleted file mode 100644 index 26dd726..0000000 --- a/ZuneModdingHelper/ReleaseVersion.cs +++ /dev/null @@ -1,484 +0,0 @@ -using CommunityToolkit.Diagnostics; -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace ZuneModdingHelper -{ - public sealed class ReleaseVersion : ICloneable, IComparable, IComparable, IEquatable - { - /// - /// Gets the value of the major component of the version number for the current object. - /// - public int Major { get; set; } - - /// - /// Gets the value of the minor component of the version number for the current object. - /// - public int Minor { get; set; } - - /// - /// Gets the value of the build component of the version number for the current object. - /// - public int Build { get; set; } - - /// - /// Gets the value of the revision component of the version number for the current object. - /// - public int Revision { get; set; } - - private Phase _Phase = Phase.Unknown; - /// - /// Gets the value of the release phase component of the version number for the current object. - /// - public Phase Phase - { - set => _Phase = value; - get - { - if (_Phase == Phase.Unknown) - { -#if DEBUG - _Phase = Phase.Debug; -#else - _Phase = Phase.Production; -#endif - } - return _Phase; - } - } - - /// - /// Initializes a new instance of the class. - /// - public ReleaseVersion() - { - } - - /// - /// Initializes a new instance of the class with the specified major, - /// minor, build, revision, and release phase. - /// - /// - /// The major version number. - /// - /// - /// The minor version number. - /// - /// - /// The build version number. - /// - /// - /// The revision version number. - /// - /// - /// The release phase. - /// - /// - /// , , , or is less than zero. - /// - public ReleaseVersion(int major, int minor, int build = 0, int revision = 0, Phase phase = Phase.Unknown) - { - Guard.IsGreaterThanOrEqualTo(major, 0, nameof(major)); - Guard.IsGreaterThanOrEqualTo(minor, 0, nameof(minor)); - Guard.IsGreaterThanOrEqualTo(build, 0, nameof(build)); - Guard.IsGreaterThanOrEqualTo(revision, 0, nameof(revision)); - - Major = major; - Minor = minor; - Build = build; - Revision = revision; - Phase = phase; - } - - /// - /// Returns a new object whose value is the same as the current - /// object. - /// - /// - /// A new whose values are a copy of the current object. - /// - public object Clone() - { - return new ReleaseVersion(Major, Minor, Build, Revision, Phase); - } - - /// - /// Compares the current object to a specified object and returns - /// an indication of their relative values. - /// - /// - /// An object to compare, or null. - /// - /// - /// A signed integer that indicates the relative values of the two objects, as shown - /// in the following table. - /// Return value – Meaning - /// Less than zero – The current object is a version before . - /// Zero – The current object is the same version as . - /// Greater than zero – The current object is a version subsequent - /// to , or is null. - /// - /// - /// is not of type . - /// - public int CompareTo(object? version) - { - if (version == null) - { - return 1; - } - - if (version is ReleaseVersion v) - { - return CompareTo(v); - } - - throw new ArgumentException(nameof(version) + " must be of type " + nameof(ReleaseVersion)); - } - - /// - /// Compares the current object to a specified object - /// and returns an indication of their relative values. - /// - /// - /// A object to compare to the current object, or null. - /// - /// - /// A signed integer that indicates the relative values of the two objects, as shown - /// in the following table. - /// Return value – Meaning - /// Less than zero – The current object is a version before . - /// Zero – The current object is the same version as . - /// Greater than zero – The current object is a version subsequent - /// to , or is null. - /// - public int CompareTo(ReleaseVersion? value) - { - return - ReferenceEquals(value, this) ? 0 : - value is null ? 1 : - Major != value.Major ? (Major > value.Major ? 1 : -1) : - Minor != value.Minor ? (Minor > value.Minor ? 1 : -1) : - Build != value.Build ? (Build > value.Build ? 1 : -1) : - Revision != value.Revision ? (Revision > value.Revision ? 1 : -1) : - Phase != value.Phase? ((byte)Phase > (byte)value.Phase ? 1 : -1) : - 0; - } - - /// - /// Returns a value indicating whether the current object is equal - /// to a specified object. - /// - /// - /// An object to compare with the current object, or null. - /// - /// - /// true if the current object and - /// are both objects, - /// and every component of the current object matches the corresponding - /// component of ; otherwise, false. - /// - public override bool Equals(object? obj) - { - return Equals(obj as ReleaseVersion); - } - - /// - /// Returns a value indicating whether the current object and a specified - /// object represent the same value. - /// - /// - /// A object to compare to the current object, or null. - /// - /// - /// true if every component of the current object matches the corresponding - /// component of the parameter; otherwise, false. - /// - public bool Equals(ReleaseVersion? obj) - { - return ReferenceEquals(obj, this) || - (obj is not null && - Major == obj.Major && - Minor == obj.Minor && - Build == obj.Build && - Revision == obj.Revision && - Phase == obj.Phase); - } - - /// - /// Returns a hash code for the current object. - /// - /// - /// A 32-bit signed integer hash code. - /// - public override int GetHashCode() - { - return HashCode.Combine(Major, Minor, Build, Revision, Phase); - } - - /// - /// Determines whether two specified objects are equal. - /// - /// - /// The first object. - /// - /// - /// The second object. - /// - /// - /// true if v1 equals v2; otherwise, false. - /// - // Force inline as the true/false ternary takes it above ALWAYS_INLINE size even though the asm ends up smaller - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool operator ==(ReleaseVersion? v1, ReleaseVersion? v2) - { - // Test "right" first to allow branch elimination when inlined for null checks (== null) - // so it can become a simple test - if (v2 is null) - { - // return true/false not the test result https://github.com/dotnet/runtime/issues/4207 - return (v1 is null) ? true : false; - } - - // Quick reference equality test prior to calling the virtual Equality - return ReferenceEquals(v2, v1) ? true : v2.Equals(v1); - } - - /// - /// Determines whether the first specified object is greater than - /// the second specified object. - /// - /// - /// The first object. - /// - /// - /// The second object. - /// - /// - /// true if v1 is greater than v2; otherwise, false. - /// - public static bool operator >(ReleaseVersion? v1, ReleaseVersion? v2) - => v2 < v1; - - /// - /// Determines whether the first specified object is greater than - /// or equal to the second specified object. - /// - /// - /// The first object. - /// - /// - /// The second object. - /// - /// - /// true if v1 is greater than or equal to v2; otherwise, false. - /// - public static bool operator >=(ReleaseVersion? v1, ReleaseVersion? v2) - => v2 <= v1; - - /// - /// Determines whether two specified objects are not equal. - /// - /// - /// The first object. - /// - /// - /// The second object. - /// - /// - /// true if v1 does not equal v2; otherwise, false. - /// - public static bool operator !=(ReleaseVersion? v1, ReleaseVersion? v2) - => !(v1 == v2); - - /// - /// Determines whether the first specified object is less than the - /// second specified object. - /// - /// - /// The first object. - /// - /// - /// The second object. - /// - /// - /// true if v1 is less than v2; otherwise, false. - /// - public static bool operator <(ReleaseVersion? v1, ReleaseVersion? v2) - { - if (v1 is null) - { - return !(v2 is null); - } - - return v1.CompareTo(v2) < 0; - } - - /// - /// Determines whether the first specified object is less than or - /// equal to the second object. - /// - /// - /// The first object. - /// - /// - /// The second object. - /// - /// - /// true if v1 is less than or equal to v2; otherwise, false. - /// - public static bool operator <=(ReleaseVersion? v1, ReleaseVersion? v2) - { - if (v1 is null) - { - return true; - } - - return v1.CompareTo(v2) <= 0; - } - - /// - /// Converts the string representation of a version number to an equivalent - /// object. - /// - /// - /// A string containing the major, minor, build, and revision numbers, where each - /// number is delimited with a period character ('.'), followed by a dash character - /// ('-') and the release phase. - /// - /// - /// An object that is equivalent to the version number specified in the input parameter. - /// - public static ReleaseVersion Parse(string input) - { - ReleaseVersion version = new(); - string versionPart = input; - int phaseIdx = input.IndexOf('-'); - if (phaseIdx != -1) - { - versionPart = input[..phaseIdx]; - - string phaseComp = input[(phaseIdx + 1)..]; - if (phaseComp.ToLowerInvariant() == "rc") - version.Phase = Phase.ReleaseCandidate; - else - version.Phase = Enum.Parse(phaseComp, true); - } - - string[] components = versionPart.Split('.'); - Guard.IsGreaterThanOrEqualTo(components.Length, 2, nameof(components)); - - if (components.Length >= 2) - { - version.Major = int.Parse(components[0]); - version.Minor = int.Parse(components[1]); - - - if (components.Length >= 3) - { - version.Build = int.Parse(components[2]); - - - if (components.Length >= 4) - { - version.Revision = int.Parse(components[3]); - } - } - } - - return version; - } - - /// - /// Converts the value of the current object to its equivalent - /// representation. - /// - /// - /// The string representation of the values of the major, minor, build, and - /// revision components of the current object, as depicted in the - /// following format. Each component is separated by a period character ('.'), - /// and the release phase is appended to the end after a dash character ('-'). Square - /// brackets ('[' and ']') indicate a component that will not appear in the return - /// value if the component is not defined: major.minor[.build[.revision]][-phase] For example, - /// if you create a object using the constructor Version(1, 1), the - /// returned string is "1.1". If you create a object using the constructor - /// Version(1, 3, 4, 2, Phase.Beta), the returned string is "1.3.4.2-beta". - /// - public override string ToString() => ToString(true); - - /// - /// - /// When set to true, the string will be formatted as major.minor.build.revision-phase, - /// even if an optional component is not present. - /// - public string ToString(bool includeAllComponents) - { - const char numDelim = '.'; - const char phaseDelim = '-'; - string output = Major.ToString() + numDelim + Minor.ToString(); - - if (Build > 0 || includeAllComponents) - { - output += numDelim + Build.ToString(); - - if (Revision > 0 || includeAllComponents) - { - output += numDelim + Revision.ToString(); - } - } - - if ((Phase != Phase.Unknown && Phase != Phase.Production) || includeAllComponents) - { - output += phaseDelim + (Phase == Phase.ReleaseCandidate ? "rc" : Phase.ToString().ToLowerInvariant()); - } - - return output; - } - - /// - /// Tries to convert the string representation of a version number to an equivalent - /// object, and returns a value that indicates whether the conversion - /// succeeded. - /// - /// - /// A string that contains a version number to convert. - /// - /// - /// When this method returns, contains the equivalent of the number - /// that is contained in input, if the conversion succeeded. If input is null, , - /// or if the conversion fails, result is null when the method returns. - /// - /// - /// true if the input parameter was converted successfully; otherwise, false. - /// - public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out ReleaseVersion? result) - { - try - { - Guard.IsNotNull(input, nameof(input)); - result = Parse(input); - return true; - } - catch - { - result = null; - return false; - } - } - } - - public enum Phase : byte - { - Unknown, - Debug, - Alpha, - Beta, - ReleaseCandidate, - Production - } -}