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());
}
}