diff --git a/.github/workflows/scala.yml b/.github/workflows/scala.yml index 87245ff..bbc6cf4 100644 --- a/.github/workflows/scala.yml +++ b/.github/workflows/scala.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - scala-version: [2.10.7, 2.11.12, 2.12.19, 2.13.14, 3.1.1] + scala-version: [2.12.19, 2.13.14, 3.1.1] steps: - name: Checkout repository and submodules uses: actions/checkout@v2 diff --git a/build.sbt b/build.sbt index e1e57e4..978c34d 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,11 @@ import ReleaseTransformations._ name := "uap-scala" organization := "org.uaparser" +scalaVersion := "2.13.14" +crossScalaVersions := Seq("2.12.19", "2.13.14", "3.1.1") + scalacOptions ++= Seq( + "-Xfatal-warnings", "-deprecation", "-encoding", "UTF-8", "-feature", @@ -11,25 +15,24 @@ scalacOptions ++= Seq( ) val scala2Flags = Seq( + "-Xlint:adapted-args", + "-Xsource:3", "-Ywarn-dead-code", "-Ywarn-numeric-widen", - "-Xfuture" + "-Ywarn-unused:imports" ) scalacOptions := { CrossVersion.partialVersion(scalaVersion.value) match { case Some((3, _)) => scalacOptions.value :+ "-language:implicitConversions" - case Some((2, scalaMajor)) if scalaMajor >= 11 => - scalacOptions.value ++ scala2Flags :+ "-Xlint:adapted-args" + case Some((2, _)) => + scalacOptions.value ++ scala2Flags case _ => - scalacOptions.value ++ scala2Flags :+ "-Yno-adapted-args" + scalacOptions.value } } -scalaVersion := "2.13.14" -crossScalaVersions := Seq("2.10.7", "2.11.12", "2.12.19", "2.13.14", "3.1.1") - libraryDependencies += "org.yaml" % "snakeyaml" % "2.2" libraryDependencies := { diff --git a/src/main/scala-2.12/org/uaparser/scala/scala.scala b/src/main/scala-2.12/org/uaparser/scala/scala.scala new file mode 100644 index 0000000..5faaaac --- /dev/null +++ b/src/main/scala-2.12/org/uaparser/scala/scala.scala @@ -0,0 +1,7 @@ +package org.uaparser + +package object scala { + object jdk { + val CollectionConverters = _root_.scala.collection.JavaConverters + } +} diff --git a/src/main/scala-2.13/org/uaparser/scala/scala.scala b/src/main/scala-2.13/org/uaparser/scala/scala.scala new file mode 100644 index 0000000..3b862ed --- /dev/null +++ b/src/main/scala-2.13/org/uaparser/scala/scala.scala @@ -0,0 +1,7 @@ +package org.uaparser + +package object scala { + object jdk { + val CollectionConverters = _root_.scala.jdk.CollectionConverters + } +} diff --git a/src/main/scala-3/org/uaparser/scala/scala.scala b/src/main/scala-3/org/uaparser/scala/scala.scala new file mode 100644 index 0000000..3b862ed --- /dev/null +++ b/src/main/scala-3/org/uaparser/scala/scala.scala @@ -0,0 +1,7 @@ +package org.uaparser + +package object scala { + object jdk { + val CollectionConverters = _root_.scala.jdk.CollectionConverters + } +} diff --git a/src/main/scala/org/uaparser/scala/Parser.scala b/src/main/scala/org/uaparser/scala/Parser.scala index 17746e7..63a1820 100644 --- a/src/main/scala/org/uaparser/scala/Parser.scala +++ b/src/main/scala/org/uaparser/scala/Parser.scala @@ -1,13 +1,9 @@ package org.uaparser.scala import java.io.InputStream -import java.util.{List => JList, Map => JMap} import org.uaparser.scala.Device.DeviceParser import org.uaparser.scala.OS.OSParser import org.uaparser.scala.UserAgent.UserAgentParser -import org.yaml.snakeyaml.{LoaderOptions, Yaml} -import org.yaml.snakeyaml.constructor.SafeConstructor -import scala.collection.JavaConverters._ import scala.util.Try case class Parser(userAgentParser: UserAgentParser, osParser: OSParser, deviceParser: DeviceParser) @@ -18,11 +14,7 @@ case class Parser(userAgentParser: UserAgentParser, osParser: OSParser, devicePa object Parser { def fromInputStream(source: InputStream): Try[Parser] = Try { - val yaml = new Yaml(new SafeConstructor(new LoaderOptions)) - val javaConfig = yaml.load[JMap[String, JList[JMap[String, String]]]](source) - val config = javaConfig.asScala.toMap.mapValues(_.asScala.toList.map(_.asScala.toMap.filterNot { - case (_ , value) => value eq null - })) + val config = YamlUtil.loadYamlAsMap(source) val userAgentParser = UserAgentParser.fromList(config.getOrElse("user_agent_parsers", Nil)) val osParser = OSParser.fromList(config.getOrElse("os_parsers", Nil)) val deviceParser = DeviceParser.fromList(config.getOrElse("device_parsers", Nil)) diff --git a/src/main/scala/org/uaparser/scala/YamlUtil.scala b/src/main/scala/org/uaparser/scala/YamlUtil.scala new file mode 100644 index 0000000..368758d --- /dev/null +++ b/src/main/scala/org/uaparser/scala/YamlUtil.scala @@ -0,0 +1,20 @@ +package org.uaparser.scala + +import java.io.InputStream +import java.util.{List => JList, Map => JMap} +import jdk.CollectionConverters._ +import org.yaml.snakeyaml.{LoaderOptions, Yaml} +import org.yaml.snakeyaml.constructor.SafeConstructor + +private[scala] object YamlUtil { + def loadYamlAsMap(yamlStream: InputStream, loader: Yaml): Map[String, List[Map[String, String]]] = { + val javaConfig = loader.load[JMap[String, JList[JMap[String, String]]]](yamlStream) + javaConfig.asScala.map { case (k, v) => + k -> v.asScala.map(_.asScala.filter{case (_, v) => v != null}.toMap).toList + }.toMap + } + + def loadYamlAsMap(yamlStream: InputStream): Map[String, List[Map[String, String]]] = { + loadYamlAsMap(yamlStream, new Yaml(new SafeConstructor(new LoaderOptions))) + } +} diff --git a/src/test/scala/org/uaparser/scala/CachingParserSpec.scala b/src/test/scala/org/uaparser/scala/CachingParserSpec.scala index 500b13c..d7cc7b7 100644 --- a/src/test/scala/org/uaparser/scala/CachingParserSpec.scala +++ b/src/test/scala/org/uaparser/scala/CachingParserSpec.scala @@ -3,6 +3,6 @@ package org.uaparser.scala import java.io.InputStream class CachingParserSpec extends ParserSpecBase { - val parser = CachingParser.default() + val parser: CachingParser = CachingParser.default() def createFromStream(stream: InputStream): UserAgentStringParser = CachingParser.fromInputStream(stream).get } diff --git a/src/test/scala/org/uaparser/scala/ParserSpec.scala b/src/test/scala/org/uaparser/scala/ParserSpec.scala index 2258ec2..1451643 100644 --- a/src/test/scala/org/uaparser/scala/ParserSpec.scala +++ b/src/test/scala/org/uaparser/scala/ParserSpec.scala @@ -3,6 +3,6 @@ package org.uaparser.scala import java.io.InputStream class ParserSpec extends ParserSpecBase { - val parser = Parser.default + val parser: Parser = Parser.default def createFromStream(stream: InputStream): UserAgentStringParser = Parser.fromInputStream(stream).get } diff --git a/src/test/scala/org/uaparser/scala/ParserSpecBase.scala b/src/test/scala/org/uaparser/scala/ParserSpecBase.scala index 39eb8e5..6d1a9c3 100644 --- a/src/test/scala/org/uaparser/scala/ParserSpecBase.scala +++ b/src/test/scala/org/uaparser/scala/ParserSpecBase.scala @@ -4,9 +4,6 @@ import org.specs2.mutable.Specification import org.yaml.snakeyaml.{LoaderOptions, Yaml} import java.io.{ByteArrayInputStream, InputStream} import java.nio.charset.StandardCharsets -import java.util.{List => JList, Map => JMap} - -import scala.collection.JavaConverters._ trait ParserSpecBase extends Specification { sequential @@ -24,8 +21,8 @@ trait ParserSpecBase extends Specification { def readCasesConfig(resource: String): List[Map[String, String]] = { val stream = this.getClass.getResourceAsStream(resource) - val cases = yaml.load[JMap[String, JList[JMap[String, String]]]](stream) - .asScala.toMap.mapValues(_.asScala.toList.map(_.asScala.toMap)) + val cases = YamlUtil.loadYamlAsMap(stream, yaml) + cases.getOrElse("test_cases", List()).filterNot(_.contains("js_ua")).map { config => config.filterNot { case (_, value) => value eq null } }