diff --git a/build.sbt b/build.sbt index 809df4c8..e6a15991 100644 --- a/build.sbt +++ b/build.sbt @@ -13,8 +13,8 @@ import com.typesafe.tools.mima.plugin.MimaKeys.{mimaBinaryIssueFilters, mimaPrev name := "scala-uri root" -ThisBuild / scalaVersion := "2.13.6" -ThisBuild / crossScalaVersions := Seq("2.12.14", scalaVersion.value) +ThisBuild / scalaVersion := "3.0.1" +ThisBuild / crossScalaVersions := Seq("2.12.14", "2.13.6", scalaVersion.value) publish / skip := true // Do not publish the root project val simulacrumScalafixVersion = "0.5.4" @@ -25,7 +25,7 @@ val sharedSettings = Seq( libraryDependencies ++= Seq( "org.typelevel" %%% "simulacrum-scalafix-annotations" % simulacrumScalafixVersion, "org.scalatest" %%% "scalatest" % "3.2.9" % Test, - "org.scalatestplus" %%% "scalacheck-1-14" % "3.2.2.0" % Test, + "org.scalatestplus" %%% "scalacheck-1-15" % "3.2.9.0" % Test, "org.scalacheck" %%% "scalacheck" % "1.15.4" % Test, "org.typelevel" %%% "cats-laws" % "2.6.1" % Test ), @@ -36,16 +36,17 @@ val sharedSettings = Seq( "utf8", "-feature", "-Xfatal-warnings", - "-language:higherKinds" + "-language:higherKinds,implicitConversions" ) ++ ( VersionNumber(scalaVersion.value) match { - case v if v.matchesSemVer(SemanticSelector(">=2.13")) => Seq("-Ymacro-annotations") + case v if v.matchesSemVer(SemanticSelector("=2.13")) => Seq("-Ymacro-annotations") case v if v.matchesSemVer(SemanticSelector("<=2.12")) => Seq("-Ypartial-unification") case _ => Nil } ), - addCompilerPlugin(scalafixSemanticdb), - scalacOptions ++= Seq(s"-P:semanticdb:targetroot:${baseDirectory.value}/target/.semanticdb", "-Yrangepos"), + semanticdbEnabled := true, +// addCompilerPlugin(scalafixSemanticdb), +// scalacOptions ++= Seq(s"-P:semanticdb:targetroot:${baseDirectory.value}/target/.semanticdb", "-Yrangepos"), Test / parallelExecution := false, scalafmtOnCompile := true, coverageExcludedPackages := "(io.lemonlabs.uri.inet.Trie.*|io.lemonlabs.uri.inet.PublicSuffixes.*|io.lemonlabs.uri.inet.PublicSuffixTrie.*|io.lemonlabs.uri.inet.PunycodeSupport.*)" @@ -63,7 +64,8 @@ val scalaUriSettings = Seq( name := "scala-uri", description := "Simple scala library for building and parsing URIs", libraryDependencies ++= Seq( - "com.chuusai" %%% "shapeless" % "2.3.7", + // TODO: Remove for3Use2_13 when scala3 version available https://github.com/milessabin/shapeless/issues/1043 + ("com.chuusai" %%% "shapeless" % "2.3.7").cross(CrossVersion.for3Use2_13), "org.typelevel" %%% "cats-core" % "2.6.1", "org.typelevel" %%% "cats-parse" % "0.3.4" ), @@ -162,7 +164,8 @@ lazy val scalaUri = Test / fork := true ) .jsSettings( - libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "1.2.0" + // TODO: Remove for3Use2_13 when scala3 version available https://github.com/scala-js/scala-js-dom/issues/451 + libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "1.2.0").cross(CrossVersion.for3Use2_13) ) lazy val docs = project diff --git a/project/UpdatePublicSuffixes.scala b/project/UpdatePublicSuffixes.scala index d0a9c696..f684013a 100644 --- a/project/UpdatePublicSuffixes.scala +++ b/project/UpdatePublicSuffixes.scala @@ -45,15 +45,15 @@ object UpdatePublicSuffixes { p.println("") p.println("object PublicSuffixes {") - p.println(" lazy val exceptions = Set(") + p.println(" lazy val exceptions: Set[String] = Set(") p.println(exceptions.map(_.tail).map(e => s""" "$e"""").mkString(",\n")) p.println(" )\n") - p.println(" lazy val wildcardPrefixes = Set(") + p.println(" lazy val wildcardPrefixes: Set[String] = Set(") p.println(wildcardPrefixes.map(_.drop(2)).map(w => s""" "$w"""").mkString(",\n")) p.println(" )\n") - p.println(" lazy val set = " + groups.keys.map(i => s"publicSuffixes$i").mkString(" ++ ")) + p.println(" lazy val set: Set[String] = " + groups.keys.map(i => s"publicSuffixes$i").mkString(" ++ ")) groups.foreach { case (index, group) => val setArgs = group.map(suffix => s""" "$suffix"""").mkString(",\n") p.println(s" private def publicSuffixes$index =\n Set(\n" + setArgs + "\n )") diff --git a/shared/src/main/scala/io/lemonlabs/uri/Authority.scala b/shared/src/main/scala/io/lemonlabs/uri/Authority.scala index 8f8ae25f..7373dca4 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/Authority.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/Authority.scala @@ -77,10 +77,10 @@ case class Authority(userInfo: Option[UserInfo], host: Host, port: Option[Int])( toString(config, _.toStringPunycode) override def toString: String = - toString(config, _.toString) + toString(config, _.toString()) def toStringRaw: String = - toString(config.withNoEncoding, _.toString) + toString(config.withNoEncoding, _.toString()) /** Returns this authority normalized according to * RFC 3986 diff --git a/shared/src/main/scala/io/lemonlabs/uri/Path.scala b/shared/src/main/scala/io/lemonlabs/uri/Path.scala index c0b8ca90..d487061d 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/Path.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/Path.scala @@ -15,7 +15,7 @@ import scala.util.Try sealed trait Path extends Product with Serializable { def config: UriConfig def parts: Vector[String] - private[uri] def toString(config: UriConfig): String + private[uri] def toStringWithConfig(config: UriConfig): String def isEmpty: Boolean def nonEmpty: Boolean = !isEmpty @@ -24,10 +24,10 @@ sealed trait Path extends Product with Serializable { * @return String containing the raw path for this Uri */ def toStringRaw: String = - toString(config.withNoEncoding) + toStringWithConfig(config.withNoEncoding) override def toString: String = - toString(config) + toStringWithConfig(config) } object Path { @@ -94,7 +94,7 @@ sealed trait UrlPath extends Path { /** Returns the encoded path. By default non ASCII characters in the path are percent encoded. * @return String containing the path for this Uri */ - private[uri] def toString(c: UriConfig): String = { + private[uri] def toStringWithConfig(c: UriConfig): String = { val encodedParts = parts.map(p => c.pathEncoder.encode(p, c.charset)) encodedParts.mkString("/") } @@ -233,7 +233,7 @@ case object EmptyPath extends AbsoluteOrEmptyPath { def unapply(path: UrlPath): Boolean = path.isEmpty - override private[uri] def toString(c: UriConfig): String = "" + override private[uri] def toStringWithConfig(c: UriConfig): String = "" override def isSlashTerminated: Boolean = false } @@ -287,8 +287,8 @@ final case class AbsolutePath(parts: Vector[String])(implicit val config: UriCon def isEmpty: Boolean = false - override private[uri] def toString(c: UriConfig): String = - "/" + super.toString(c) + override private[uri] def toStringWithConfig(c: UriConfig): String = + "/" + super.toStringWithConfig(c) override def isSlashTerminated: Boolean = parts.lastOption.fold(true)(_ == "") @@ -314,7 +314,7 @@ final case class UrnPath(nid: String, nss: String)(implicit val config: UriConfi def isEmpty: Boolean = false - private[uri] def toString(c: UriConfig): String = + private[uri] def toStringWithConfig(c: UriConfig): String = nid + ":" + c.pathEncoder.encode(nss, c.charset) } diff --git a/shared/src/main/scala/io/lemonlabs/uri/Uri.scala b/shared/src/main/scala/io/lemonlabs/uri/Uri.scala index d181a4fe..e215b438 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/Uri.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/Uri.scala @@ -72,7 +72,7 @@ sealed trait Uri extends Product with Serializable { * @return a `java.net.URI` matching this `io.lemonlabs.uri.Uri` */ def toJavaURI: java.net.URI = - new java.net.URI(toString(config)) + new java.net.URI(toStringWithConfig(config)) /** Similar to `==` but ignores the ordering of any query string parameters */ @@ -82,12 +82,12 @@ sealed trait Uri extends Product with Serializable { * @return String containing the raw path for this Uri */ def toStringRaw: String = - toString(config.withNoEncoding) + toStringWithConfig(config.withNoEncoding) override def toString: String = - toString(config) + toStringWithConfig(config) - private[uri] def toString(config: UriConfig): String + private[uri] def toStringWithConfig(config: UriConfig): String } object Uri { @@ -479,7 +479,7 @@ sealed trait Url extends Uri { * RFC 3490. */ def toStringPunycode: String = - toString(config) + toStringWithConfig(config) protected def queryToString(config: UriConfig): String = query.toString(config) match { @@ -488,7 +488,7 @@ sealed trait Url extends Uri { } def toRedactedString(redactor: Redactor)(implicit conf: UriConfig = UriConfig.default): String = - redactor.apply(this).toString(conf) + redactor.apply(this).toStringWithConfig(conf) /** Similar to `==` but ignores the ordering of any query string parameters */ @@ -657,8 +657,8 @@ final case class RelativeUrl(path: UrlPath, query: QueryString, fragment: Option def withQueryString(query: QueryString): RelativeUrl = copy(query = query) - private[uri] def toString(c: UriConfig): String = - path.toString(c) + queryToString(c) + fragmentToString(c) + private[uri] def toStringWithConfig(c: UriConfig): String = + path.toStringWithConfig(c) + queryToString(c) + fragmentToString(c) def removeUserInfo(): RelativeUrl = this def removePassword(): RelativeUrl = this @@ -844,12 +844,12 @@ sealed trait UrlWithAuthority extends Url { * RFC 3490. */ override def toStringPunycode: String = - toString(config, _.toStringPunycode) + toStringWithConfig(config, _.toStringPunycode) - private[uri] def toString(c: UriConfig): String = - toString(c, _.toString) + private[uri] def toStringWithConfig(c: UriConfig): String = + toStringWithConfig(c, _.toString()) - private[uri] def toString(c: UriConfig, hostToString: Host => String): String + private[uri] def toStringWithConfig(c: UriConfig, hostToString: Host => String): String } object UrlWithAuthority { @@ -913,8 +913,8 @@ final case class ProtocolRelativeUrl(authority: Authority, def withQueryString(query: QueryString): ProtocolRelativeUrl = copy(query = query) - private[uri] def toString(c: UriConfig, hostToString: Host => String): String = - "//" + authority.toString(c, hostToString) + path.toString(c) + queryToString(c) + fragmentToString(c) + private[uri] def toStringWithConfig(c: UriConfig, hostToString: Host => String): String = + "//" + authority.toString(c, hostToString) + path.toStringWithConfig(c) + queryToString(c) + fragmentToString(c) def normalize(removeEmptyPathParts: Boolean = false, slashTermination: SlashTermination = SlashTermination.AddForEmptyPath @@ -988,8 +988,10 @@ final case class AbsoluteUrl(scheme: String, def withQueryString(query: QueryString): AbsoluteUrl = copy(query = query) - private[uri] def toString(c: UriConfig, hostToString: Host => String): String = - scheme + "://" + authority.toString(c, hostToString) + path.toString(c) + queryToString(c) + fragmentToString(c) + private[uri] def toStringWithConfig(c: UriConfig, hostToString: Host => String): String = + scheme + "://" + authority.toString(c, hostToString) + path.toStringWithConfig(c) + queryToString( + c + ) + fragmentToString(c) def normalize(removeEmptyPathParts: Boolean = false, slashTermination: SlashTermination = SlashTermination.AddForEmptyPath @@ -1132,8 +1134,8 @@ final case class SimpleUrlWithoutAuthority(scheme: String, path: UrlPath, query: def withQueryString(query: QueryString): SimpleUrlWithoutAuthority = copy(query = query) - private[uri] def toString(c: UriConfig): String = - scheme + ":" + path.toString(c) + queryToString(c) + fragmentToString(c) + private[uri] def toStringWithConfig(c: UriConfig): String = + scheme + ":" + path.toStringWithConfig(c) + queryToString(c) + fragmentToString(c) def normalize(removeEmptyPathParts: Boolean = false, slashTermination: SlashTermination = SlashTermination.AddForEmptyPath @@ -1240,7 +1242,7 @@ final case class DataUrl(mediaType: MediaType, base64: Boolean, data: Array[Byte def withQueryString(query: QueryString): DataUrl = this - private[uri] def toString(c: UriConfig): String = + private[uri] def toStringWithConfig(c: UriConfig): String = scheme + ":" + pathString(c) override def equals(obj: Any): Boolean = obj match { @@ -1342,9 +1344,9 @@ final case class ScpLikeUrl(override val user: Option[String], override val host def withScheme(scheme: String): AbsoluteUrl = AbsoluteUrl(scheme, authority, path.toAbsoluteOrEmpty, QueryString.empty, None) - private[uri] def toString(c: UriConfig, hostToString: Host => String): String = { + private[uri] def toStringWithConfig(c: UriConfig, hostToString: Host => String): String = { // Don't do percent encoding. Can't find any reference to it being - user.fold("")(_ + "@") + hostToString(host) + ":" + path.toString(config.withNoEncoding) + user.fold("")(_ + "@") + hostToString(host) + ":" + path.toStringWithConfig(config.withNoEncoding) } /** For ScpLikeUrls this method is exactly the same as `==` @@ -1402,8 +1404,8 @@ final case class Urn(path: UrnPath)(implicit val config: UriConfig = UriConfig.d def toUrl: Url = throw new UriConversionException("Urn cannot be converted to Url") def toUrn: Urn = this - private[uri] def toString(c: UriConfig): String = - scheme + ":" + path.toString(c) + private[uri] def toStringWithConfig(c: UriConfig): String = + scheme + ":" + path.toStringWithConfig(c) /** For URNs this method is exactly the same as `==` */ diff --git a/shared/src/main/scala/io/lemonlabs/uri/config/UriConfig.scala b/shared/src/main/scala/io/lemonlabs/uri/config/UriConfig.scala index 4db4a85b..b83eae6b 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/config/UriConfig.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/config/UriConfig.scala @@ -17,56 +17,6 @@ case class UriConfig(userInfoEncoder: UriEncoder, defaultPorts: Map[String, Int] ) { - def this(userInfoEncoder: UriEncoder, - pathEncoder: UriEncoder, - queryEncoder: UriEncoder, - fragmentEncoder: UriEncoder, - userInfoDecoder: UriDecoder, - pathDecoder: UriDecoder, - queryDecoder: UriDecoder, - fragmentDecoder: UriDecoder, - charset: String, - renderQuery: RenderQuery - ) = - this( - userInfoEncoder, - pathEncoder, - queryEncoder, - fragmentEncoder, - userInfoDecoder, - pathDecoder, - queryDecoder, - fragmentDecoder, - charset, - renderQuery, - UriConfig.defaultPorts - ) - - def copy(userInfoEncoder: UriEncoder = this.userInfoEncoder, - pathEncoder: UriEncoder = this.pathEncoder, - queryEncoder: UriEncoder = this.queryEncoder, - fragmentEncoder: UriEncoder = this.fragmentEncoder, - userInfoDecoder: UriDecoder = this.userInfoDecoder, - pathDecoder: UriDecoder = this.pathDecoder, - queryDecoder: UriDecoder = this.queryDecoder, - fragmentDecoder: UriDecoder = this.fragmentDecoder, - charset: String = this.charset, - renderQuery: RenderQuery = this.renderQuery - ): UriConfig = - UriConfig( - userInfoEncoder, - pathEncoder, - queryEncoder, - fragmentEncoder, - userInfoDecoder, - pathDecoder, - queryDecoder, - fragmentDecoder, - charset, - renderQuery, - defaultPorts - ) - def withDefaultPorts(newDefaultPorts: Map[String, Int]): UriConfig = UriConfig( userInfoEncoder, diff --git a/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala b/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala index 5d56e3f7..b3677d60 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala @@ -1,7 +1,7 @@ package io.lemonlabs.uri.inet object PublicSuffixes { - lazy val exceptions = Set( + lazy val exceptions: Set[String] = Set( "www.ck", "city.kawasaki.jp", "city.kitakyushu.jp", @@ -12,7 +12,7 @@ object PublicSuffixes { "city.yokohama.jp" ) - lazy val wildcardPrefixes = Set( + lazy val wildcardPrefixes: Set[String] = Set( "bd", "nom.br", "ck", @@ -33,7 +33,7 @@ object PublicSuffixes { "sch.uk" ) - lazy val set = publicSuffixes0 ++ publicSuffixes1 + lazy val set: Set[String] = publicSuffixes0 ++ publicSuffixes1 private def publicSuffixes0 = Set( "ac", @@ -620,7 +620,6 @@ object PublicSuffixes { "md.ci", "gouv.ci", "cl", - "aprendemas.cl", "co.cl", "gob.cl", "gov.cl", @@ -5035,11 +5034,11 @@ object PublicSuffixes { "org.pe", "com.pe", "net.pe", - "pf" + "pf", + "com.pf" ) private def publicSuffixes1 = Set( - "com.pf", "org.pf", "edu.pf", "ph", @@ -6011,6 +6010,7 @@ object PublicSuffixes { "edu.vc", "ve", "arts.ve", + "bib.ve", "co.ve", "com.ve", "e12.ve", @@ -6022,7 +6022,9 @@ object PublicSuffixes { "int.ve", "mil.ve", "net.ve", + "nom.ve", "org.ve", + "rar.ve", "rec.ve", "store.ve", "tec.ve", @@ -6721,6 +6723,7 @@ object PublicSuffixes { "kerryproperties", "kfh", "kia", + "kids", "kim", "kinder", "kindle", diff --git a/shared/src/main/scala/io/lemonlabs/uri/typesafe/Fragment.scala b/shared/src/main/scala/io/lemonlabs/uri/typesafe/Fragment.scala index 7f19b41e..2ddf019a 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/typesafe/Fragment.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/typesafe/Fragment.scala @@ -71,7 +71,7 @@ sealed trait FragmentInstances1 extends FragmentInstances2 { implicit final val floatQueryValue: Fragment[Float] = stringFragment.contramap(_.toString) implicit final val doubleQueryValue: Fragment[Double] = stringFragment.contramap(_.toString) implicit final val uuidQueryValue: Fragment[java.util.UUID] = stringFragment.contramap(_.toString) - implicit val noneFragment: Fragment[None.type] = identity + implicit val noneFragment: Fragment[None.type] = _ => None } sealed trait FragmentInstances extends FragmentInstances1 { diff --git a/shared/src/test/scala/io/lemonlabs/uri/EncodingTests.scala b/shared/src/test/scala/io/lemonlabs/uri/EncodingTests.scala index a2c358d7..c94de179 100644 --- a/shared/src/test/scala/io/lemonlabs/uri/EncodingTests.scala +++ b/shared/src/test/scala/io/lemonlabs/uri/EncodingTests.scala @@ -30,7 +30,7 @@ class EncodingTests extends AnyFlatSpec with Matchers { "URI path double quotes" should "be percent encoded when using conservative encoder" in { val url = Url.parse("""http://theon.github.com/blah/"quoted"""") - url.toString(UriConfig.conservative) should equal("http://theon.github.com/blah/%22quoted%22") + url.toStringWithConfig(UriConfig.conservative) should equal("http://theon.github.com/blah/%22quoted%22") } "URI path spaces" should "be plus encoded if configured" in { @@ -53,14 +53,14 @@ class EncodingTests extends AnyFlatSpec with Matchers { "Querystring double quotes" should "be percent encoded when using conservative encoder" in { val url = Url.parse("""http://theon.github.com?blah="quoted"""") - url.toString(UriConfig.conservative) should equal("http://theon.github.com?blah=%22quoted%22") + url.toStringWithConfig(UriConfig.conservative) should equal("http://theon.github.com?blah=%22quoted%22") } "Reserved characters" should "be percent encoded when using conservative encoder" in { val url = Url( query = QueryString.fromPairs("reserved" -> ":/?#[]@!$&'()*+,;={}\\\n\r") ) - url.toString(UriConfig.conservative) should equal( + url.toStringWithConfig(UriConfig.conservative) should equal( "?reserved=%3A%2F%3F%23%5B%5D%40%21%24%26%27%28%29%2A%2B%2C%3B%3D%7B%7D%5C%0A%0D" ) } diff --git a/shared/src/test/scala/io/lemonlabs/uri/NapGithubIssueTests.scala b/shared/src/test/scala/io/lemonlabs/uri/NapGithubIssueTests.scala index b0220e39..bfc71de5 100644 --- a/shared/src/test/scala/io/lemonlabs/uri/NapGithubIssueTests.scala +++ b/shared/src/test/scala/io/lemonlabs/uri/NapGithubIssueTests.scala @@ -15,7 +15,7 @@ import org.scalatest.matchers.should.Matchers class NapGithubIssueTests extends AnyFlatSpec with Matchers with OptionValues { "Github Issue #2" should "now be fixed. Pluses in querystrings should be encoded when using the conservative encoder" in { val uri = Url.parse("http://theon.github.com/").addParam("+", "+") - uri.toString(UriConfig.conservative) should equal("http://theon.github.com/?%2B=%2B") + uri.toStringWithConfig(UriConfig.conservative) should equal("http://theon.github.com/?%2B=%2B") } "Github Issue #4" should "now be fixed. Port numbers should be rendered by toString" in { diff --git a/shared/src/test/scala/io/lemonlabs/uri/ToUriTests.scala b/shared/src/test/scala/io/lemonlabs/uri/ToUriTests.scala index d3d70604..8cd32dd9 100644 --- a/shared/src/test/scala/io/lemonlabs/uri/ToUriTests.scala +++ b/shared/src/test/scala/io/lemonlabs/uri/ToUriTests.scala @@ -71,7 +71,7 @@ class ToUriTests extends AnyWordSpec with Matchers { url.password should equal(Some("password")) url.path.toString() should equal("/test") url.query.params should equal(Vector(("weird=&key", Some("strange%value")), ("arrow", Some("⇔")))) - url.toString(UriConfig.conservative) should equal(javaUri.toASCIIString) + url.toStringWithConfig(UriConfig.conservative) should equal(javaUri.toASCIIString) } } }