@@ -13,18 +13,18 @@ package com.demonwav.mcdev.util
1313import com.demonwav.mcdev.util.SemanticVersion.Companion.VersionPart.PreReleasePart
1414import com.demonwav.mcdev.util.SemanticVersion.Companion.VersionPart.ReleasePart
1515
16- class SemanticVersion (val value : String ) : Comparable<SemanticVersion> {
17- val parts = value.split(" ." ).map {
18- if (it.contains(" _pre" )) {
19- val subParts = it.split(" _pre" )
20- PreReleasePart (subParts[0 ].toInt(), subParts[1 ].toInt())
21- } else {
22- ReleasePart (it.toInt())
23- }
24- }
25-
16+ /* *
17+ * Represents a comparable and generalised "semantic version".
18+ * Each constituent part (delimited by periods in a version string) contributes
19+ * to the version ranking with decreasing priority from left to right.
20+ */
21+ class SemanticVersion (val parts : List <VersionPart >) : Comparable<SemanticVersion> {
2622 override fun compareTo (other : SemanticVersion ): Int {
27- val result = parts.zip(other.parts).fold(0 , { acc, (a, b) -> if (acc == - 1 ) acc else a.compareTo(b) })
23+ // Zipping limits the compared parts to the shorter version, then we perform a component-wise comparison
24+ // Short-circuits if any component of this version is smaller/older
25+ val result = parts.zip(other.parts).fold(0 ) { acc, (a, b) -> if (acc == - 1 ) acc else a.compareTo(b) }
26+ // When all the parts are equal, the longer version wins
27+ // Generally speaking, 1.0 is considered older than 1.0.1 (see MC 1.12 vs 1.12.1)
2828 if (parts.size != other.parts.size && result == 0 ) {
2929 return parts.size - other.parts.size
3030 }
@@ -37,18 +37,48 @@ class SemanticVersion(val value: String) : Comparable<SemanticVersion> {
3737 else -> false
3838 }
3939
40- override fun hashCode (): Int {
41- return parts.hashCode()
42- }
40+ override fun hashCode () = parts.hashCode()
41+
42+ override fun toString () = parts.map { it.toString() }.joinToString( " . " )
4343
4444 companion object {
45+ /* *
46+ * Parses a version string into a comparable representation.
47+ * @throws IllegalArgumentException if any part of the version string cannot be parsed as integer or split into pre-release parts.
48+ */
49+ fun parse (value : String ): SemanticVersion {
50+ fun parseInt (part : String ): Int =
51+ if (part.all { it.isDigit() })
52+ part.toInt()
53+ else
54+ throw IllegalArgumentException (" Failed to parse version part as integer: $part " )
55+
56+ val parts = value.split(' .' ).map {
57+ if (it.contains(" _pre" )) {
58+ // There have been cases of Forge builds for MC pre-releases (1.7.10_pre4)
59+ // We're consuming the 10_pre4 and extracting 10 and 4 from it
60+ val subParts = it.split(" _pre" )
61+ if (subParts.size == 2 ) {
62+ PreReleasePart (parseInt(subParts[0 ]), parseInt(subParts[1 ]))
63+ } else {
64+ throw IllegalArgumentException (" Failed to split pre-release version part into two numbers: $it " )
65+ }
66+ } else {
67+ ReleasePart (parseInt(it))
68+ }
69+ }
70+ return SemanticVersion (parts)
71+ }
72+
4573 sealed class VersionPart : Comparable <VersionPart > {
4674 data class ReleasePart (val version : Int ) : VersionPart() {
4775 override fun compareTo (other : VersionPart ) =
4876 when (other) {
4977 is PreReleasePart -> if (version != other.version) version - other.version else 1
5078 is ReleasePart -> version - other.version
5179 }
80+
81+ override fun toString () = version.toString()
5282 }
5383
5484 data class PreReleasePart (val version : Int , val pre : Int ) : VersionPart() {
@@ -57,6 +87,8 @@ class SemanticVersion(val value: String) : Comparable<SemanticVersion> {
5787 is PreReleasePart -> if (version != other.version) version - other.version else pre - other.pre
5888 is ReleasePart -> if (version != other.version) version - other.version else - 1
5989 }
90+
91+ override fun toString () = " ${version} _pre$pre "
6092 }
6193 }
6294 }
0 commit comments