Skip to content

Commit

Permalink
Refactor ScalaTargetType/ScalaTarget
Browse files Browse the repository at this point in the history
The refactor in this commit is driven by the work in the subsequent
commit for issue scalacenter#659 (creating a Scaladex badge that supplies
concise info on supported Scala versions). The key word there is
'concise' - given the many Scala artifacts that can be published for
a single artifact version (the matrix of Scala language version, the
platform - Scala JS/Native/SBT - and the platform version), achieving
a concise summary requires a lot of grouping.

Grouping by Scala `LanguageVersion` was easy enough, but grouping by
platform & platform version was harder, as there wasn't any existing
class to represent that. Consequently, I introduced the concept of a
'platform edition' (eg 'Scala JS 0.6', or 'Scala Native 0.4'), which
is composed of two values:

* targetType (Scala JS, Scala Native, or SBT)
* platform version (eg the version '0.6' or '1.0' of Scala JS)

...this is like a `ScalaTarget`, but without the Scala Language
version that `ScalaTarget` includes (eg a `ScalaTarget` is
specifically compiled for, eg, _Scala 2.11_ on Scala JS 0.6).

Having introduced the concept of `PlatformEdition`, it made sense to
redefine relevant `ScalaTarget`s in terms of that - to do this meant
moving several ad-hoc methods from the companion objects of
`ScalaTarget` implementations (ie `ScalaJs`, `ScalaNative`,
`SbtPlugin`) to the corresponding `ScalaTargetType` implementations
and making them more widely available, by declaring them in the
`PlatformVersionedTargetType`. This removed duplication of this code:

* producing a String `render` value for platform names
* checking validity of platform versions.

A lot of repetition was removed, and in fact most of the `ScalaTarget`
companion objects were now empty and could be deleted.

Another smaller change was prompted by the upcoming need to identify
a `ScalaTargetType` by name (when the user specifies it as a query
string parameter). To do that, you need a full list of
`ScalaTargetType`s, and then you can also use that list to create an
`Ordering[ScalaTargetType]`

Also tidied up various tests that were creating their own platform
version constants (as I was already moving equivalent constants from
`ScalaTarget` companion objects to `ScalaTargetType` objects)
  • Loading branch information
rtyley committed May 3, 2021
1 parent a4d0498 commit 2bc70e4
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ import ch.epfl.scala.index.data.maven.PomsReader
import ch.epfl.scala.index.data.project.ArtifactMetaExtractor
import ch.epfl.scala.index.model.misc.Sha1
import ch.epfl.scala.index.model.release.{
Js,
MinorBinary,
Native,
Sbt,
SbtPlugin,
ScalaVersion,
ScalaJs,
ScalaJvm,
ScalaNative
ScalaNative,
ScalaVersion
}
import de.heikoseeberger.akkahttpjson4s.Json4sSupport._
import org.joda.time.DateTime
Expand Down Expand Up @@ -257,23 +260,18 @@ class CentralMissing(paths: DataPaths)(implicit val system: ActorSystem) {
.flatten
.toSet

val sbt013 = MinorBinary(0, 13)
val sbt10 = MinorBinary(1, 0)
val scalaJs06 = MinorBinary(0, 6)
val native03 = MinorBinary(0, 3)

val allTargets = List(
ScalaJvm(ScalaVersion.`2.13`),
ScalaJvm(ScalaVersion.`2.12`),
ScalaJvm(ScalaVersion.`2.11`),
ScalaJvm(ScalaVersion.`2.10`),
SbtPlugin(ScalaVersion.`2.10`, sbt013),
SbtPlugin(ScalaVersion.`2.12`, sbt10),
ScalaJs(ScalaVersion.`2.13`, scalaJs06),
ScalaJs(ScalaVersion.`2.12`, scalaJs06),
ScalaJs(ScalaVersion.`2.11`, scalaJs06),
ScalaJs(ScalaVersion.`2.10`, scalaJs06),
ScalaNative(ScalaVersion.`2.11`, native03)
SbtPlugin(ScalaVersion.`2.10`, Sbt.`0.13`),
SbtPlugin(ScalaVersion.`2.12`, Sbt.`1.0`),
ScalaJs(ScalaVersion.`2.13`, Js.`0.6`),
ScalaJs(ScalaVersion.`2.12`, Js.`0.6`),
ScalaJs(ScalaVersion.`2.11`, Js.`0.6`),
ScalaJs(ScalaVersion.`2.10`, Js.`0.6`),
ScalaNative(ScalaVersion.`2.11`, Native.`0.3`)
)

val releasesDownloads =
Expand Down
14 changes: 8 additions & 6 deletions model/src/main/scala/ch.epfl.scala.index.model/Project.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package ch.epfl.scala.index.model

import ch.epfl.scala.index.model.release.{
BinaryVersion,
Js,
LanguageVersion,
Native,
Sbt,
SbtPlugin,
ScalaJs,
ScalaJvm,
ScalaNative
}
import misc.{GithubInfo, GithubRepo, Url}
import ch.epfl.scala.index.model.release.LanguageVersion
import ch.epfl.scala.index.model.release.BinaryVersion

/**
* Project representation which contains all necessary meta data to
Expand Down Expand Up @@ -76,12 +79,11 @@ case class Project(
id = None,
scalaVersion = LanguageVersion.sortFamilies(scalaVersion),
scalaJsVersion =
BinaryVersion.sortAndFilter(scalaJsVersion, ScalaJs.isValid).toList,
BinaryVersion.sortAndFilter(scalaJsVersion, Js.isValid).toList,
scalaNativeVersion = BinaryVersion
.sortAndFilter(scalaNativeVersion, ScalaNative.isValid)
.sortAndFilter(scalaNativeVersion, Native.isValid)
.toList,
sbtVersion =
BinaryVersion.sortAndFilter(sbtVersion, SbtPlugin.isValid).toList
sbtVersion = BinaryVersion.sortAndFilter(sbtVersion, Sbt.isValid).toList
)
}

Expand Down
195 changes: 102 additions & 93 deletions model/src/main/scala/ch.epfl.scala.index.model/release/ScalaTarget.scala
Original file line number Diff line number Diff line change
@@ -1,26 +1,94 @@
package ch.epfl.scala.index.model
package release

import ch.epfl.scala.index.model.release
import fastparse.NoWhitespace._
import fastparse._

object ScalaTargetType {
implicit val ordering: Ordering[ScalaTargetType] = Ordering.by {
case Jvm => 5
case Js => 4
case Native => 3
case Sbt => 2
case Java => 1
}

val All = Seq(Java, Sbt, Native, Js, Jvm)

implicit val ordering: Ordering[ScalaTargetType] = Ordering.by(All.indexOf(_))

}

sealed trait ScalaTargetType
case object Jvm extends ScalaTargetType
case object Js extends ScalaTargetType
case object Native extends ScalaTargetType

case class PlatformEdition(
targetType: PlatformVersionedTargetType,
version: BinaryVersion
) {
val isValid: Boolean = targetType.isValid(version)
val render: String = s"${targetType.platformId} $version"
}

case class PlatformAndLanguageVersions(
language: LanguageVersion,
platform: BinaryVersion
)

sealed trait PlatformVersionedTargetType extends ScalaTargetType {
def encode(platformAndLanguageVersions: PlatformAndLanguageVersions): String
val platformId: String
val stableBinaryVersions: Set[BinaryVersion]
def isValid(version: BinaryVersion): Boolean =
stableBinaryVersions.contains(version)
def Parser[_: P]: P[ScalaTarget]
}
case object Js extends PlatformVersionedTargetType {
val platformId = "scala-js"
def encode(versions: PlatformAndLanguageVersions) =
s"_sjs${versions.platform}_${versions.language}"

val `0.6`: BinaryVersion = MinorBinary(0, 6)
val `1.x`: BinaryVersion = MajorBinary(1)

val stableBinaryVersions = Set(`0.6`, `1.x`)

def Parser[_: P]: P[ScalaTarget] =
("_sjs" ~ BinaryVersion.Parser ~ "_" ~ LanguageVersion.Parser).map {
case (scalaJsVersion, scalaVersion) =>
ScalaJs(scalaVersion, scalaJsVersion)
}
}
case object Native extends PlatformVersionedTargetType {
val platformId = "scala-native"
def encode(versions: PlatformAndLanguageVersions) =
s"_native${versions.platform}_${versions.language}"
val `0.3`: BinaryVersion = MinorBinary(0, 3)
val `0.4`: BinaryVersion = MinorBinary(0, 4)

val stableBinaryVersions = Set(`0.3`, `0.4`)

def Parser[_: P]: P[ScalaTarget] =
("_native" ~ BinaryVersion.Parser ~ "_" ~ LanguageVersion.Parser).map {
case (scalaNativeVersion, scalaVersion) =>
ScalaNative(scalaVersion, scalaNativeVersion)
}
}
case object Java extends ScalaTargetType
case object Sbt extends ScalaTargetType
case object Sbt extends PlatformVersionedTargetType {
val platformId = "sbt"
def encode(versions: PlatformAndLanguageVersions) =
s"_${versions.language}_${versions.platform}"

val `0.13`: BinaryVersion = MinorBinary(0, 13)
val `1.0`: BinaryVersion = MinorBinary(1, 0)

val stableBinaryVersions = Set(`0.13`, `1.0`)

def Parser[_: P]: P[ScalaTarget] =
("_" ~ LanguageVersion.Parser ~ "_" ~ BinaryVersion.Parser.filter(isValid))
.map { case (scalaVersion, sbtVersion) =>
SbtPlugin(scalaVersion, sbtVersion)
} | ("_" ~ BinaryVersion.Parser.filter(
isValid
) ~ "_" ~ LanguageVersion.Parser)
.map { case (scalaVersion, sbtVersion) =>
SbtPlugin(sbtVersion, scalaVersion)
}
}

/*
SemanticVersion will only contain the information from the artifactId
Expand All @@ -41,45 +109,42 @@ sealed trait ScalaTarget extends Ordered[ScalaTarget] {
ScalaTarget.ordering.compare(this, that)
}

sealed trait ScalaTargetWithPlatformBinaryVersion extends ScalaTarget {
val platformEdition: PlatformEdition
lazy val targetType: PlatformVersionedTargetType = platformEdition.targetType

lazy val isValid: Boolean = languageVersion.isValid && platformEdition.isValid

lazy val render = s"${platformEdition.render} ($languageVersion)"
lazy val platformAndLanguageVersions: PlatformAndLanguageVersions =
PlatformAndLanguageVersions(languageVersion, platformEdition.version)
lazy val encode: String = targetType.encode(platformAndLanguageVersions)
}

case class ScalaJvm(languageVersion: LanguageVersion) extends ScalaTarget {
val render = languageVersion.render
val render: String = languageVersion.render
val encode = s"_$languageVersion"
val targetType: ScalaTargetType = Jvm
val isValid: Boolean = languageVersion.isValid
}
case class ScalaJs(
languageVersion: LanguageVersion,
scalaJsVersion: BinaryVersion
) extends ScalaTarget {
val render = s"scala-js $scalaJsVersion (${languageVersion.render})"
val encode = s"_sjs${scalaJsVersion}_$languageVersion"
val targetType: ScalaTargetType = Js
val isValid: Boolean = languageVersion.isValid && ScalaJs.isValid(
scalaJsVersion
)
) extends ScalaTargetWithPlatformBinaryVersion {
val platformEdition = PlatformEdition(Js, scalaJsVersion)
}
case class ScalaNative(
languageVersion: LanguageVersion,
scalaNativeVersion: BinaryVersion
) extends ScalaTarget {
val render = s"scala-native $scalaNativeVersion (${languageVersion.render})"
val encode = s"_native${scalaNativeVersion}_$languageVersion"
val targetType: ScalaTargetType = Native
val isValid: Boolean = languageVersion.isValid && ScalaNative.isValid(
scalaNativeVersion
)
) extends ScalaTargetWithPlatformBinaryVersion {
val platformEdition = PlatformEdition(Native, scalaNativeVersion)
}

case class SbtPlugin(
languageVersion: LanguageVersion,
sbtVersion: BinaryVersion
) extends ScalaTarget {
val render = s"sbt $sbtVersion ($languageVersion)"
val encode = s"_${languageVersion}_$sbtVersion"
val targetType: ScalaTargetType = Sbt
val isValid: Boolean = languageVersion.isValid && SbtPlugin.isValid(
sbtVersion
)
) extends ScalaTargetWithPlatformBinaryVersion {
val platformEdition = PlatformEdition(Sbt, sbtVersion)
}

object ScalaJvm {
Expand All @@ -102,72 +167,16 @@ object ScalaJvm {
("_" ~ LanguageVersion.Parser).map(ScalaJvm.apply)
}

object ScalaJs {
val `0.6`: BinaryVersion = MinorBinary(0, 6)
val `1.x`: BinaryVersion = MajorBinary(1)

private val stableBinaryVersions = Set(`0.6`, `1.x`)

def isValid(version: BinaryVersion): Boolean =
stableBinaryVersions.contains(version)

def Parser[_: P]: P[ScalaTarget] =
("_sjs" ~ BinaryVersion.Parser ~ "_" ~ LanguageVersion.Parser).map {
case (scalaJsVersion, scalaVersion) =>
ScalaJs(scalaVersion, scalaJsVersion)
}
}

object ScalaNative {
val `0.3`: BinaryVersion = MinorBinary(0, 3)
val `0.4`: BinaryVersion = MinorBinary(0, 4)

private val stableBinaryVersions = Set(`0.3`, `0.4`)

def isValid(version: BinaryVersion): Boolean =
stableBinaryVersions.contains(version)

def Parser[_: P]: P[ScalaTarget] =
("_native" ~ BinaryVersion.Parser ~ "_" ~ LanguageVersion.Parser).map {
case (scalaNativeVersion, scalaVersion) =>
ScalaNative(scalaVersion, scalaNativeVersion)
}
}

object SbtPlugin {
val `0.13`: BinaryVersion = MinorBinary(0, 13)
val `1.0`: BinaryVersion = MinorBinary(1, 0)

private val stableBinaryVersions = Set(`0.13`, `1.0`)

def isValid(version: BinaryVersion): Boolean = {
stableBinaryVersions.contains(version)
}

def Parser[_: P]: P[ScalaTarget] =
("_" ~ LanguageVersion.Parser ~ "_" ~ BinaryVersion.Parser.filter(isValid))
.map { case (scalaVersion, sbtVersion) =>
SbtPlugin(scalaVersion, sbtVersion)
} | ("_" ~ BinaryVersion.Parser.filter(
isValid
) ~ "_" ~ LanguageVersion.Parser)
.map { case (scalaVersion, sbtVersion) =>
SbtPlugin(sbtVersion, scalaVersion)
}
}

object ScalaTarget extends Parsers {
// Scala > Js > Native > Sbt
implicit val ordering: Ordering[ScalaTarget] =
Ordering.by[
ScalaTarget,
(ScalaTargetType, Option[BinaryVersion], LanguageVersion)
] {
case ScalaJvm(version) => (Jvm, None, version)
case ScalaJs(version, jsVersion) => (Js, Some(jsVersion), version)
case ScalaNative(version, nativeVersion) =>
(Native, Some(nativeVersion), version)
case SbtPlugin(version, sbtVersion) => (Sbt, Some(sbtVersion), version)
case st: ScalaTargetWithPlatformBinaryVersion =>
(st.targetType, Some(st.platformEdition.version), st.languageVersion)
case st: ScalaTarget => (st.targetType, None, st.languageVersion)
}

def parse(code: String): Option[ScalaTarget] = {
Expand All @@ -178,5 +187,5 @@ object ScalaTarget extends Parsers {
Parser ~ End

def Parser[_: P]: P[ScalaTarget] =
ScalaJs.Parser | ScalaNative.Parser | SbtPlugin.Parser | ScalaJvm.Parser
Js.Parser | Native.Parser | Sbt.Parser | ScalaJvm.Parser
}
17 changes: 4 additions & 13 deletions model/src/test/scala/ch.epfl.scala.index.model/ArtifactTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ class ArtifactTests extends FunSpec with Matchers {
Artifact.parse("cats-core_sjs0.6_2.11") should contain(
Artifact(
"cats-core",
ScalaJs(
languageVersion = ScalaVersion.`2.11`,
scalaJsVersion = MinorBinary(0, 6)
)
ScalaJs(ScalaVersion.`2.11`, Js.`0.6`)
)
)
}
Expand All @@ -21,10 +18,7 @@ class ArtifactTests extends FunSpec with Matchers {
Artifact.parse("cats-core_native0.1_2.11") should contain(
Artifact(
"cats-core",
ScalaNative(
languageVersion = ScalaVersion.`2.11`,
scalaNativeVersion = MinorBinary(0, 1)
)
ScalaNative(ScalaVersion.`2.11`, MinorBinary(0, 1))
)
)
}
Expand All @@ -51,15 +45,12 @@ class ArtifactTests extends FunSpec with Matchers {
Artifact.parse("sbt-microsites_2.12_1.0") should contain(
Artifact(
"sbt-microsites",
SbtPlugin(
languageVersion = ScalaVersion.`2.12`,
sbtVersion = MinorBinary(1, 0)
)
SbtPlugin(ScalaVersion.`2.12`, Sbt.`1.0`)
)
)
}

it("does not parse unconventionnal") {
it("does not parse unconventional") {
Artifact.parse("sparrow") shouldBe empty
}

Expand Down
Loading

0 comments on commit 2bc70e4

Please sign in to comment.