-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[proposal] Literal Scala versions #16
base: main
Are you sure you want to change the base?
Changes from all commits
bccfc4a
fbf1830
8e6c52b
a3890f0
96cffa2
ce128f2
8374eec
6a0ddc8
585837e
be4cec5
28703e0
5b18f11
8c6593c
b4876aa
bcf9579
da7c020
40cbc47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* Copyright 2022 Typelevel | ||
* | ||
* 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. | ||
*/ | ||
|
||
package org.typelevel.scalacoptions | ||
|
||
import org.typelevel.scalacoptions.internal.Parser | ||
|
||
import scala.Ordering.Implicits._ | ||
import scala.collection.immutable | ||
|
||
abstract sealed case class ScalaVersion private (major: Long, minor: Long, patch: Long) { | ||
|
||
def isBetween(addedVersion: ScalaVersion, removedVersion: ScalaVersion): Boolean = | ||
this >= addedVersion && this < removedVersion | ||
} | ||
|
||
object ScalaVersion { | ||
private def apply(major: Long, minor: Long, patch: Long): ScalaVersion = | ||
new ScalaVersion(major, minor, patch) {} | ||
|
||
lazy val knownVersions: immutable.SortedSet[ScalaVersion] = { | ||
val bld = immutable.SortedSet.newBuilder[ScalaVersion] | ||
|
||
def bldPatches(major: Long, minor: Long, lastPatch: Long): Unit = | ||
for (patch <- 0L to lastPatch) | ||
bld += ScalaVersion(major, minor, patch) | ||
|
||
bldPatches(2, 11, 12) | ||
bldPatches(2, 12, 17) | ||
bldPatches(2, 13, 9) | ||
bldPatches(3, 0, 2) | ||
bldPatches(3, 1, 3) | ||
bldPatches(3, 2, 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could cause problems that this list must be maintained by hand. I wonder if we can come up with some way that Scala Steward could recognise these versions and bump them, e.g. by using a String here instead of major,minor,patch? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather not get into a position where we have to release a new version of this library (and all downstream tooling based on it) for every new Scala version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call out. I tried to avoid enumerating all known versions by hand, but for the sake of to be understood by scala-steward we could create a list of all known versions and put it into a separate file:
and then use it from the ScalaVersion object:
or something like that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just leave it open ended? So unless a series is known to be EOL it will accept not-yet-released versions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @armanbilge just to make sure I understand you correctly, you would prefer to avoid having scala-steward involved into that completely, wouldn't you? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Right, it's not that I care about Scala Steward specifically, but rather that we would have to release a new version of this library (and thus a new version of all the plugins that use it) for every new Scala version.
I don't think that's necessary. But I do think it should accept |
||
|
||
bld.result() | ||
} | ||
|
||
def from(major: Long, minor: Long, patch: Long): Option[ScalaVersion] = { | ||
val candidate = ScalaVersion(major, minor, patch) | ||
if (knownVersions.contains(candidate)) Some(candidate) else None | ||
} | ||
|
||
def unsafeFrom(major: Long, minor: Long, patch: Long): ScalaVersion = | ||
from(major, minor, patch).getOrElse(throw invalidScalaVersionError(s"$major.$minor.$patch")) | ||
|
||
object fromString { | ||
def apply(s: String): Option[ScalaVersion] = | ||
Parser.parseGenericVersion(s).flatMap(Function.tupled(from _)) | ||
def unapply(s: String): Option[ScalaVersion] = apply(s) | ||
|
||
def unsafe(s: String): ScalaVersion = apply(s).getOrElse(throw invalidScalaVersionError(s)) | ||
} | ||
|
||
@deprecated("use literal `sver\"2.11.0\"` instead", "<TBD>") | ||
val V2_11_0 = ScalaVersion(2, 11, 0) | ||
@deprecated("use literal `sver\"2.11.11\"` instead", "<TBD>") | ||
val V2_11_11 = ScalaVersion(2, 11, 11) | ||
@deprecated("use literal `sver\"2.12.0\"` instead", "<TBD>") | ||
val V2_12_0 = ScalaVersion(2, 12, 0) | ||
@deprecated("use literal `sver\"2.12.2\"` instead", "<TBD>") | ||
val V2_12_2 = ScalaVersion(2, 12, 2) | ||
@deprecated("use literal `sver\"2.12.5\"` instead", "<TBD>") | ||
val V2_12_5 = ScalaVersion(2, 12, 5) | ||
@deprecated("use literal `sver\"2.12.13\"` instead", "<TBD>") | ||
val V2_12_13 = ScalaVersion(2, 12, 13) | ||
@deprecated("use literal `sver\"2.13.0\"` instead", "<TBD>") | ||
val V2_13_0 = ScalaVersion(2, 13, 0) | ||
@deprecated("use literal `sver\"2.13.2\"` instead", "<TBD>") | ||
val V2_13_2 = ScalaVersion(2, 13, 2) | ||
@deprecated("use literal `sver\"2.13.3\"` instead", "<TBD>") | ||
val V2_13_3 = ScalaVersion(2, 13, 3) | ||
@deprecated("use literal `sver\"2.13.4\"` instead", "<TBD>") | ||
val V2_13_4 = ScalaVersion(2, 13, 4) | ||
@deprecated("use literal `sver\"2.13.5\"` instead", "<TBD>") | ||
val V2_13_5 = ScalaVersion(2, 13, 5) | ||
@deprecated("use literal `sver\"2.13.6\"` instead", "<TBD>") | ||
val V2_13_6 = ScalaVersion(2, 13, 6) | ||
@deprecated("use literal `sver\"3.0.0\"` instead", "<TBD>") | ||
val V3_0_0 = ScalaVersion(3, 0, 0) | ||
@deprecated("use literal `sver\"3.1.0\"` instead", "<TBD>") | ||
val V3_1_0 = ScalaVersion(3, 1, 0) | ||
|
||
implicit lazy val scalaVersionOrdering: Ordering[ScalaVersion] = | ||
Ordering.by(version => (version.major, version.minor, version.patch)) | ||
|
||
private def invalidScalaVersionError(s: String) = | ||
new IllegalArgumentException(s"invalid Scala version: $s") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright 2022 Typelevel | ||
* | ||
* 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. | ||
*/ | ||
|
||
package org.typelevel.scalacoptions.internal | ||
|
||
private[scalacoptions] object Parser { | ||
private[internal] lazy val genericVersionRe = """^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$""".r | ||
|
||
private object asLong { | ||
def unapply(s: String): Option[Long] = { | ||
try { | ||
Some(java.lang.Long.parseUnsignedLong(s)) | ||
} catch { | ||
case _: NumberFormatException => None | ||
} | ||
} | ||
} | ||
|
||
def parseGenericVersion(s: String): Option[(Long, Long, Long)] = s match { | ||
case genericVersionRe(asLong(major), asLong(minor), asLong(patch)) => | ||
Some((major, minor, patch)) | ||
case _ => | ||
None | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way for us to check that
literally
doesn't end up on our users' runtime classpath when depending upon this library? Perhaps I'm being paranoid 😛There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... I like your idea, but is it feasible in principle? I'll try to figure out that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't necessarily mean an automated way to do this but it would be nice to create a project that depends on this library and check the
runtimeClasspath
in sbt 😃