@ -1,9 +1,11 @@
using IPA.Config.Data ;
# nullable enable
using IPA.Config.Data ;
using IPA.Config.Stores ;
using IPA.Config.Stores ;
using IPA.Config.Stores.Converters ;
using IPA.Config.Stores.Converters ;
using System ;
using System ;
using System.Collections.Generic ;
using System.Collections.Generic ;
using Version = SemVer . Version ;
using SVersion = SemVer . Version ;
using Version = Hive . Versioning . Version ;
namespace IPA.Utilities
namespace IPA.Utilities
{
{
@ -11,7 +13,10 @@ namespace IPA.Utilities
/// A type that wraps <see cref="Version"/> so that the string of the version is stored when the string is
/// A type that wraps <see cref="Version"/> so that the string of the version is stored when the string is
/// not a valid <see cref="Version"/>.
/// not a valid <see cref="Version"/>.
/// </summary>
/// </summary>
public class AlmostVersion : IComparable < AlmostVersion > , IComparable < Version >
public class AlmostVersion : IComparable < AlmostVersion > , IComparable < Version > ,
#pragma warning disable CS0618 // Type or member is obsolete
IComparable < SVersion >
#pragma warning restore CS0618 // Type or member is obsolete
{
{
/// <summary>
/// <summary>
/// Represents a storage type of either parsed <see cref="Version"/> object or raw <see cref="String"/>.
/// Represents a storage type of either parsed <see cref="Version"/> object or raw <see cref="String"/>.
@ -19,7 +24,7 @@ namespace IPA.Utilities
public enum StoredAs
public enum StoredAs
{
{
/// <summary>
/// <summary>
/// The version was stored as a <see cref="Version"/>.
/// The version was stored as a <see cref="S Version"/>.
/// </summary>
/// </summary>
SemVer ,
SemVer ,
/// <summary>
/// <summary>
@ -35,7 +40,7 @@ namespace IPA.Utilities
public AlmostVersion ( string vertext )
public AlmostVersion ( string vertext )
{
{
if ( ! TryParseFrom ( vertext , StoredAs . SemVer ) )
if ( ! TryParseFrom ( vertext , StoredAs . SemVer ) )
TryParseFrom ( vertext , StoredAs . String ) ;
_ = TryParseFrom ( vertext , StoredAs . String ) ;
}
}
/// <summary>
/// <summary>
@ -46,7 +51,14 @@ namespace IPA.Utilities
{
{
SemverValue = ver ;
SemverValue = ver ;
StorageMode = StoredAs . SemVer ;
StorageMode = StoredAs . SemVer ;
}
}
/// <summary>
/// Creates an <see cref="AlmostVersion"/> from the <see cref="SVersion"/> provided in <paramref name="ver"/>.
/// </summary>
/// <param name="ver">the <see cref="SVersion"/> to store</param>
[Obsolete("Use Hive.Versioning.Version constructor instead.")]
public AlmostVersion ( SVersion ver ) : this ( ver ? . UnderlyingVersion ? ? throw new ArgumentNullException ( nameof ( ver ) ) ) { }
/// <summary>
/// <summary>
/// Creates an <see cref="AlmostVersion"/> from the version string in <paramref name="vertext"/> stored using
/// Creates an <see cref="AlmostVersion"/> from the version string in <paramref name="vertext"/> stored using
@ -68,26 +80,22 @@ namespace IPA.Utilities
/// <param name="copyMode">an <see cref="AlmostVersion"/> to copy the storage mode of</param>
/// <param name="copyMode">an <see cref="AlmostVersion"/> to copy the storage mode of</param>
public AlmostVersion ( string vertext , AlmostVersion copyMode )
public AlmostVersion ( string vertext , AlmostVersion copyMode )
{
{
if ( copyMode = = null )
if ( copyMode is null )
throw new ArgumentNullException ( nameof ( copyMode ) ) ;
throw new ArgumentNullException ( nameof ( copyMode ) ) ;
if ( ! TryParseFrom ( vertext , copyMode . StorageMode ) )
if ( ! TryParseFrom ( vertext , copyMode . StorageMode ) )
TryParseFrom ( vertext , StoredAs . String ) ; // silently parse differently
_ = TryParseFrom ( vertext , StoredAs . String ) ; // silently parse differently
}
}
private bool TryParseFrom ( string str , StoredAs mode )
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 ;
}
if ( mode = = StoredAs . SemVer )
{
StorageMode = StoredAs . SemVer ;
var result = Version . TryParse ( str , out var version ) ;
SemverValue = version ;
return result ;
}
else
else
{
{
StringValue = str ;
StringValue = str ;
@ -100,13 +108,13 @@ namespace IPA.Utilities
/// The value of the <see cref="AlmostVersion"/> if it was stored as a <see cref="string"/>.
/// The value of the <see cref="AlmostVersion"/> if it was stored as a <see cref="string"/>.
/// </summary>
/// </summary>
/// <value>the stored value as a <see cref="string"/>, or <see langword="null"/> if not stored as a string.</value>
/// <value>the stored value as a <see cref="string"/>, or <see langword="null"/> if not stored as a string.</value>
public string StringValue { get ; private set ; } = null ;
public string? StringValue { get ; private set ; }
/// <summary>
/// <summary>
/// The value of the <see cref="AlmostVersion"/> if it was stored as a <see cref="Version"/>.
/// The value of the <see cref="AlmostVersion"/> if it was stored as a <see cref="Version"/>.
/// </summary>
/// </summary>
/// <value>the stored value as a <see cref="Version"/>, or <see langword="null"/> if not stored as a version.</value>
/// <value>the stored value as a <see cref="Version"/>, or <see langword="null"/> if not stored as a version.</value>
public Version SemverValue { get ; private set ; } = null ;
public Version ? SemverValue { get ; private set ; }
/// <summary>
/// <summary>
/// The way the value is stored, whether it be as a <see cref="Version"/> or a <see cref="string"/>.
/// The way the value is stored, whether it be as a <see cref="Version"/> or a <see cref="string"/>.
@ -114,15 +122,14 @@ namespace IPA.Utilities
/// <value>the storage mode used to store this value</value>
/// <value>the storage mode used to store this value</value>
public StoredAs StorageMode { get ; private set ; }
public StoredAs StorageMode { get ; private set ; }
// can I just <inheritdoc /> this?
/// <summary>
/// <summary>
/// Gets a string representation of the current version. If the value is stored as a string, this returns it. If it is
/// 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 <see cref="Version"/>, it is equivalent to calling <see cref="Version.ToString"/>.
/// stored as a <see cref="Version"/>, it is equivalent to calling <see cref="Version.ToString() "/>.
/// </summary>
/// </summary>
/// <returns>a string representation of the current version</returns>
/// <returns>a string representation of the current version</returns>
/// <seealso cref="object.ToString"/>
/// <seealso cref="object.ToString"/>
public override string ToString ( ) = >
public override string ToString ( ) = >
StorageMode = = StoredAs . SemVer ? SemverValue . ToString ( ) : StringValue ;
StorageMode = = StoredAs . SemVer ? SemverValue ! . ToString ( ) : StringValue ! ;
/// <summary>
/// <summary>
/// Compares <see langword="this"/> to the <see cref="AlmostVersion"/> in <paramref name="other"/> using <see cref="Version.CompareTo(Version)"/>
/// Compares <see langword="this"/> to the <see cref="AlmostVersion"/> in <paramref name="other"/> using <see cref="Version.CompareTo(Version)"/>
@ -136,14 +143,11 @@ namespace IPA.Utilities
/// <seealso cref="CompareTo(Version)"/>
/// <seealso cref="CompareTo(Version)"/>
public int CompareTo ( AlmostVersion other )
public int CompareTo ( AlmostVersion other )
{
{
if ( other = = null ) return - 1 ;
if ( StorageMode ! = other . StorageMode )
throw new InvalidOperationException ( "Cannot compare AlmostVersions with different stores!" ) ;
if ( other is null ) return 1 ;
if ( StorageMode = = StoredAs . SemVer )
return SemverValue . CompareTo ( other . SemverValue ) ;
else
return StringValue . CompareTo ( other . StringValue ) ;
return StorageMode = = StoredAs . SemVer & & other . StorageMode = = StoredAs . SemVer
? SemverValue ! . CompareTo ( other . SemverValue ! )
: string . Compare ( ToString ( ) , other . ToString ( ) , StringComparison . Ordinal ) ;
}
}
/// <summary>
/// <summary>
@ -161,8 +165,21 @@ namespace IPA.Utilities
if ( StorageMode ! = StoredAs . SemVer )
if ( StorageMode ! = StoredAs . SemVer )
throw new InvalidOperationException ( "Cannot compare a SemVer version with an AlmostVersion stored as a string!" ) ;
throw new InvalidOperationException ( "Cannot compare a SemVer version with an AlmostVersion stored as a string!" ) ;
return SemverValue . CompareTo ( other ) ;
}
return SemverValue ! . CompareTo ( other ) ;
}
/// <summary>
/// Compares <see langword="this"/> to the <see cref="SVersion"/> in <paramref name="other"/> using <see cref="Version.CompareTo(Version)"/>.
/// </summary>
/// <remarks>
/// The storage method of <see langword="this"/> must be <see cref="StoredAs.SemVer"/>, else an <see cref="InvalidOperationException"/> will
/// be thrown.
/// </remarks>
/// <param name="other">the <see cref="SVersion"/> to compare to</param>
/// <returns>less than 0 if <paramref name="other"/> is considered bigger than <see langword="this"/>, 0 if equal, and greater than zero if smaller</returns>
/// <seealso cref="CompareTo(AlmostVersion)"/>
[Obsolete("Use the Hive.Versioning.Version overload instead.")]
public int CompareTo ( SVersion other ) = > CompareTo ( other . UnderlyingVersion ) ;
/// <summary>
/// <summary>
/// Performs a strict equality check between <see langword="this"/> and <paramref name="obj"/>.
/// Performs a strict equality check between <see langword="this"/> and <paramref name="obj"/>.
@ -189,12 +206,12 @@ namespace IPA.Utilities
public override int GetHashCode ( )
public override int GetHashCode ( )
{
{
var hashCode = - 1 2 6 4 0 2 8 9 7 ;
var hashCode = - 1 2 6 4 0 2 8 9 7 ;
hashCode = hashCode * - 1 5 2 1 1 3 4 2 9 5 + EqualityComparer < Version > . Default . GetHashCode ( SemverValue ) ;
hashCode = hashCode * - 1 5 2 1 1 3 4 2 9 5 + EqualityComparer < string > . Default . GetHashCode ( StringValue ) ;
hashCode = hashCode * - 1 5 2 1 1 3 4 2 9 5 + StorageMode . GetHashCode ( ) ;
hashCode = ( hashCode * - 1 5 2 1 1 3 4 2 9 5 ) + EqualityComparer < Version ? > . Default . GetHashCode ( SemverValue ) ;
hashCode = ( hashCode * - 1 5 2 1 1 3 4 2 9 5 ) + EqualityComparer < string? > . Default . GetHashCode ( StringValue ) ;
hashCode = ( hashCode * - 1 5 2 1 1 3 4 2 9 5 ) + StorageMode . GetHashCode ( ) ;
return hashCode ;
return hashCode ;
}
}
/// <summary>
/// <summary>
/// Compares two versions, only taking into account the numeric part of the version if they are stored as <see cref="Version"/>s,
/// Compares two versions, only taking into account the numeric part of the version if they are stored as <see cref="Version"/>s,
/// or strict equality if they are stored as <see cref="string"/>s.
/// or strict equality if they are stored as <see cref="string"/>s.
@ -212,10 +229,9 @@ namespace IPA.Utilities
if ( l is null & & r is null ) return true ;
if ( l is null & & r is null ) return true ;
if ( l is null | | r is null ) return false ;
if ( l is null | | r is null ) return false ;
if ( l . StorageMode ! = r . StorageMode ) 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 ;
return l . StorageMode = = StoredAs . SemVer
? Utils . VersionCompareNoPrerelease ( l . SemverValue ! , r . SemverValue ! ) = = 0
: l . StringValue = = r . StringValue ;
}
}
/// <summary>
/// <summary>
@ -225,24 +241,57 @@ namespace IPA.Utilities
/// <param name="r">the second value to compare</param>
/// <param name="r">the second value to compare</param>
/// <returns><see langword="true"/> if they are not mostly equal, <see langword="false"/> otherwise</returns>
/// <returns><see langword="true"/> if they are not mostly equal, <see langword="false"/> otherwise</returns>
/// <seealso cref="operator ==(AlmostVersion, AlmostVersion)"/>
/// <seealso cref="operator ==(AlmostVersion, AlmostVersion)"/>
public static bool operator ! = ( AlmostVersion l , AlmostVersion r ) = > ! ( l = = r ) ;
// implicitly convertible from Version
public static bool operator ! = ( AlmostVersion l , AlmostVersion r ) = > ! ( l = = r ) ;
// implicitly convertible from Version
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CA2225 // Operator overloads have named alternates
/// <summary>
/// <summary>
/// Implicitly converts a <see cref="Version"/> to <see cref="AlmostVersion"/> using <see cref="AlmostVersion(Version)"/>.
/// Implicitly converts a <see cref="S Version"/> to <see cref="AlmostVersion"/> using <see cref="AlmostVersion(S Version)"/>.
/// </summary>
/// </summary>
/// <param name="ver">the <see cref="Version"/> to convert</param>
/// <seealso cref="AlmostVersion(Version)"/>
public static implicit operator AlmostVersion ( Version ver ) = > new AlmostVersion ( ver ) ;
// implicitly convertible to Version
/// <param name="ver">the <see cref="SVersion"/> to convert</param>
/// <seealso cref="AlmostVersion(SVersion)"/>
[Obsolete("Use Hive.Versioning.Version instead of SemVer.Version")]
public static implicit operator AlmostVersion ? ( SVersion ? ver ) = > ver is null ? null : new ( ver ) ;
// implicitly convertible to Version
/// <summary>
/// Implicitly converts an <see cref="AlmostVersion"/> to <see cref="SVersion"/>, if applicable, using <see cref="SemverValue"/>.
/// If not applicable, returns <see langword="null"/>
/// </summary>
/// <param name="av">the <see cref="AlmostVersion"/> to convert to a <see cref="SVersion"/></param>
/// <seealso cref="SemverValue"/>
[Obsolete("Use Hive.Versioning.Version instead of SemVer.Version")]
public static implicit operator SVersion ? ( AlmostVersion ? av ) = > av ? . SemverValue is not null ? SVersion . ForHiveVersion ( av . SemverValue ) : null ;
#pragma warning restore CS0618 // Type or member is obsolete
/// <summary>
/// Implicitly converts a <see cref="SVersion"/> to <see cref="AlmostVersion"/> using <see cref="AlmostVersion(SVersion)"/>.
/// </summary>
/// <param name="ver">the <see cref="SVersion"/> to convert</param>
/// <seealso cref="AlmostVersion(SVersion)"/>
public static implicit operator AlmostVersion ? ( Version ? ver ) = > ver is null ? null : new ( ver ) ;
// implicitly convertible to Version
/// <summary>
/// <summary>
/// Implicitly converts an <see cref="AlmostVersion"/> to <see cref="Version"/>, if applicable, using <see cref="SemverValue"/>.
/// Implicitly converts an <see cref="AlmostVersion"/> to <see cref="S Version"/>, if applicable, using <see cref="SemverValue"/>.
/// If not applicable, returns <see langword="null"/>
/// If not applicable, returns <see langword="null"/>
/// </summary>
/// </summary>
/// <param name="av">the <see cref="AlmostVersion"/> to convert to a <see cref="Version"/></param>
/// <param name="av">the <see cref="AlmostVersion"/> to convert to a <see cref="S Version"/></param>
/// <seealso cref="SemverValue"/>
/// <seealso cref="SemverValue"/>
public static implicit operator Version ( AlmostVersion av ) = > av ? . SemverValue ;
public static implicit operator Version ? ( AlmostVersion av ) = > av ? . SemverValue ;
#pragma warning restore CA2225 // Operator overloads have named alternates
public static bool operator < ( AlmostVersion left , AlmostVersion right )
= > left is null ? right is not null : left . CompareTo ( right ) < 0 ;
public static bool operator < = ( AlmostVersion left , AlmostVersion right )
= > left is null | | left . CompareTo ( right ) < = 0 ;
public static bool operator > ( AlmostVersion left , AlmostVersion right )
= > left is not null & & left . CompareTo ( right ) > 0 ;
public static bool operator > = ( AlmostVersion left , AlmostVersion right )
= > left is null ? right is null : left . CompareTo ( right ) > = 0 ;
}
}
/// <summary>
/// <summary>
@ -257,7 +306,7 @@ namespace IPA.Utilities
/// <param name="parent">the owner of the new object</param>
/// <param name="parent">the owner of the new object</param>
/// <returns></returns>
/// <returns></returns>
public override AlmostVersion FromValue ( Value value , object parent )
public override AlmostVersion FromValue ( Value value , object parent )
= > new AlmostVersion ( Converter < string > . Default . FromValue ( value , parent ) ) ;
= > new ( Converter < string > . Default . FromValue ( value , parent ) ) ;
/// <summary>
/// <summary>
/// Converts an <see cref="AlmostVersion"/> to a <see cref="Text"/> node.
/// Converts an <see cref="AlmostVersion"/> to a <see cref="Text"/> node.
/// </summary>
/// </summary>