diff --git a/versions/shared/src/main/scala/coursier/version/Version.scala b/versions/shared/src/main/scala/coursier/version/Version.scala index f8eb02e..8a9a4f7 100644 --- a/versions/shared/src/main/scala/coursier/version/Version.scala +++ b/versions/shared/src/main/scala/coursier/version/Version.scala @@ -10,16 +10,57 @@ import scala.annotation.tailrec * * Same kind of ordering as aether-util/src/main/java/org/eclipse/aether/util/version/GenericVersion.java */ -@data class Version(repr: String) extends Ordered[Version] { +final class Version(val repr: String) extends Ordered[Version] with Product with Serializable { lazy val items: Vector[Version.Item] = Version.items(repr) - def compare(other: Version) = Version.listCompare(items, other.items) + def isEmpty = items.forall(_.isEmpty) + + // We can't use @data here or the equals method will be inconsistent with + // the compare method. This means we have to hand write these methods. + + def withRepr(value: String): Version = + new Version(value) + + override def compare(other: Version) = Version.listCompare(items, other.items) + + override def canEqual(that: Any): Boolean = + that match { + case _: Version => + true + case _ => + false + } + + override def equals(that: Any): Boolean = + this.canEqual(that) && (that match { + case that: Version => + this.compare(that) == 0 + case _ => false + }) + + override val productArity: Int = 1 + + override def productElement(n: Int): Any = + if (n == 0) { + repr + } else { + throw new IndexOutOfBoundsException(n) + } + + override def toString: String = + s"Version(${repr})" + + override def hashCode: Int = + Version.items(repr).filterNot(Version.isBuildMetadata).filterNot(_.isEmpty).hashCode } object Version { private[version] val zero = Version("0") + def apply(repr: String): Version = + new Version(repr) + sealed abstract class Item extends Ordered[Item] { def compare(other: Item): Int = (this, other) match { diff --git a/versions/shared/src/test/scala/coursier/version/VersionTests.scala b/versions/shared/src/test/scala/coursier/version/VersionTests.scala index a8d72de..24f7476 100644 --- a/versions/shared/src/test/scala/coursier/version/VersionTests.scala +++ b/versions/shared/src/test/scala/coursier/version/VersionTests.scala @@ -8,6 +8,17 @@ object VersionTests extends TestSuite { def compare(first: String, second: String) = Version(first).compare(Version(second)) + def compareEqualsConsistent(first: String, second: String): Boolean = { + val firstV: Version = Version(first) + val secondV: Version = Version(second) + + if (firstV.compare(secondV) == 0) { + firstV == secondV && firstV.hashCode == secondV.hashCode + } else { + firstV != secondV + } + } + def increasing(versions: String*): Boolean = versions.iterator.sliding(2).withPartial(false).forall{case Seq(a, b) => compare(a, b) < 0 } @@ -86,6 +97,12 @@ object VersionTests extends TestSuite { assert(compare("0", "" ) == 0) } + "compareIsConsistentWithEquals" - { + assert(compareEqualsConsistent("1.0.0", "1.0.0")) + assert(compareEqualsConsistent("1.0.0", "1.0.0.0")) + assert(compareEqualsConsistent("1.0.0", "1")) + assert(compareEqualsConsistent("1.0.0", "2.0.0")) + } "numericOrdering" - { assert(compare("2", "10" ) < 0)