/*
* Copyright 2018 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Diagnostics;
namespace CommonUtil {
///
/// Version number container. Instances are immutable.
///
///
/// See https://semver.org/ for explanation of system.
///
public struct Version {
// Must be in ascending order, e.g. Alpha release comes before Beta.
public enum PreRelType { Dev, Alpha, Beta, Final };
///
/// Major version number.
///
public int Major { get; private set; }
///
/// Minor version number.
///
public int Minor { get; private set; }
///
/// Bug fix release number.
///
public int Patch { get; private set; }
///
/// Software grade, for pre-release versions.
///
public PreRelType PreReleaseType { get; private set; }
///
/// Pre-release version. Always zero when PreReleaseType is Final.
///
public int PreRelease { get; private set; }
///
/// Version instance to use when no version information is available. This will
/// always compare as less than a "real" version.
///
public static readonly Version NO_VERSION = new Version(-1, -1, -1);
///
/// Shortcut for comparing vs. NO_VERSION.
///
public bool IsValid {
get {
return this != NO_VERSION;
}
}
public Version(int major, int minor) :
this(major, minor, 0, PreRelType.Final, 0) { }
public Version(int major, int minor, int patch) :
this(major, minor, patch, PreRelType.Final, 0) { }
public Version(int major, int minor, int patch, PreRelType preRelType, int preRel) {
Debug.Assert(preRelType != PreRelType.Final || preRel == 0);
Major = major;
Minor = minor;
Patch = patch;
PreReleaseType = preRelType;
PreRelease = preRel;
}
///
/// Attempts to parse the argument into version components.
///
/// Version string.
/// New Version object, or NO_VERSION on parsing failure.
public static Version Parse(string str) {
try {
int major, minor, patch;
major = minor = patch = 0;
string[] parts = str.Split(new char[] { '.', '-' });
major = int.Parse(parts[0]);
if (parts.Length > 1) {
minor = int.Parse(parts[1]);
}
if (parts.Length > 2) {
patch = int.Parse(parts[2]);
}
// parse the preRel thing someday
return new Version(major, minor, patch);
} catch (Exception ex) {
Debug.WriteLine("Version parse failed: '" + str + "': " + ex.Message);
return NO_VERSION;
}
}
// this is a struct, so no need for null checks
public static bool operator ==(Version a, Version b) {
return a.Major == b.Major && a.Minor == b.Minor && a.Patch == b.Patch &&
a.PreReleaseType == b.PreReleaseType && a.PreRelease == b.PreRelease;
}
public static bool operator !=(Version a, Version b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is Version && this == (Version)obj;
}
public override int GetHashCode() {
return Major * 10000 + Minor * 1000 + Patch * 100 +
(int)PreReleaseType * 10 + PreRelease;
}
public static bool operator <(Version a, Version b) {
if (a.Major != b.Major) {
return a.Major < b.Major;
}
if (a.Minor != b.Minor) {
return a.Minor < b.Minor;
}
if (a.Patch != b.Patch) {
return a.Patch < b.Patch;
}
if (a.PreReleaseType != b.PreReleaseType) {
return (int)a.PreReleaseType < (int)b.PreReleaseType;
}
if (a.PreRelease != b.PreRelease) {
return a.PreRelease < b.PreRelease;
}
Debug.Assert(a == b);
return false;
}
public static bool operator >(Version a, Version b) {
return b < a;
}
public static bool operator <=(Version a, Version b) {
return a == b || a < b;
}
public static bool operator >=(Version a, Version b) {
return a == b || a > b;
}
public override string ToString() {
if (this == NO_VERSION) {
return Properties.Resources.NO_VERSION;
} else if (PreReleaseType == PreRelType.Final) {
return string.Format("{0}.{1}.{2}", Major, Minor, Patch);
} else {
return string.Format("{0}.{1}.{2}-{3}{4}", Major, Minor, Patch,
PreReleaseType.ToString().ToLower(), PreRelease);
}
}
///
/// Simple unit test.
///
public static bool Test() {
bool ok = true;
Version checkVers = new Version(1, 2, 3, PreRelType.Beta, 4);
Version sameVers = new Version(1, 2, 3, PreRelType.Beta, 4);
ok &= (checkVers == sameVers);
ok &= (checkVers <= sameVers);
ok &= (checkVers >= sameVers);
ok &= (!(checkVers < sameVers));
ok &= (!(checkVers > sameVers));
ok &= (checkVers != new Version(1, 2, 3));
ok &= (checkVers < new Version(1, 2, 3));
ok &= (checkVers > new Version(1, 2, 3, PreRelType.Beta, 3));
ok &= (checkVers < new Version(2, 0));
ok &= (checkVers > new Version(1, 2, 2));
ok &= (checkVers < new Version(1, 3, 1));
Debug.WriteLine("Version struct: test complete (ok=" + ok + ")");
return ok;
}
}
}