From 9f13499bcf5703479ce9dfdadfe1192b844a4f17 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Mar 2017 13:10:17 +0000 Subject: [PATCH 01/19] Don't double wrap setting value Oh the wonderful world of Object#toString --- .../main/scala/sbt/internal/server/NetworkChannel.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 953a2d57a..cd06fba21 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -84,7 +84,7 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil val key = Parser.parse(req.setting, SettingQuery.scopedKeyParser(structure, currentBuild)) - def getSettingValue[A](key: Def.ScopedKey[A]) = + def getSettingValue[A](key: Def.ScopedKey[A]): Either[String, A] = structure.data.get(key.scope, key.key) .toRight(s"Key ${Def displayFull key} not found") .flatMap { @@ -93,12 +93,12 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil case x => Right(x) } - val values = key match { + val values: Either[String, Any] = key match { case Left(msg) => Left(s"Invalid programmatic input: $msg") - case Right(key) => Right(getSettingValue(key)) + case Right(key) => getSettingValue(key) } - val jsonValues = values match { + val jsonValues: String = values match { case Left(errors) => errors case Right(value) => value.toString } From 8081661a7da7591481ac18b9fd9dbdc7a2036914 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Mar 2017 13:15:12 +0000 Subject: [PATCH 02/19] Fix ordering --- .../sbt/internal/server/NetworkChannel.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index cd06fba21..82b98cc38 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -150,15 +150,6 @@ object SettingQuery { } yield Act.taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask) } - def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] = - scopedKey( - structure.index.keyIndex, - currentBuild, - structure.extra.configurationsForAxis, - structure.index.keyMap, - structure.data - ) - def scopedKeySelected( index: KeyIndex, currentBuild: URI, @@ -178,4 +169,13 @@ object SettingQuery { data: Settings[Scope] ): Parser[ScopedKey[_]] = scopedKeySelected(index, currentBuild, defaultConfigs, keyMap, data).map(_.key) + + def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] = + scopedKey( + structure.index.keyIndex, + currentBuild, + structure.extra.configurationsForAxis, + structure.index.keyMap, + structure.data + ) } From 4566c615c7988f6161c73db4aedb23505e0a877a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Mar 2017 13:16:20 +0000 Subject: [PATCH 03/19] Move setting query things into SettingQuery --- .../sbt/internal/server/NetworkChannel.scala | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 82b98cc38..72e3ce2cc 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -79,32 +79,8 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil private def onExecCommand(cmd: ExecCommand) = append(Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) - private def onSettingQuery(req: SettingQuery) = { - import sbt.internal.util.complete.Parser - - val key = Parser.parse(req.setting, SettingQuery.scopedKeyParser(structure, currentBuild)) - - def getSettingValue[A](key: Def.ScopedKey[A]): Either[String, A] = - structure.data.get(key.scope, key.key) - .toRight(s"Key ${Def displayFull key} not found") - .flatMap { - case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") - case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings") - case x => Right(x) - } - - val values: Either[String, Any] = key match { - case Left(msg) => Left(s"Invalid programmatic input: $msg") - case Right(key) => getSettingValue(key) - } - - val jsonValues: String = values match { - case Left(errors) => errors - case Right(value) => value.toString - } - - StandardMain.exchange publishEventMessage SettingQueryResponse(jsonValues) - } + private def onSettingQuery(req: SettingQuery) = + StandardMain.exchange publishEventMessage SettingQuery.handleSettingQuery(req, structure, currentBuild) def shutdown(): Unit = { println("Shutting down client connection") @@ -178,4 +154,29 @@ object SettingQuery { structure.index.keyMap, structure.data ) + + def handleSettingQuery(req: SettingQuery, structure: BuildStructure, currentBuild: URI): SettingQueryResponse = { + val key = Parser.parse(req.setting, scopedKeyParser(structure, currentBuild)) + + def getSettingValue[A](key: Def.ScopedKey[A]): Either[String, A] = + structure.data.get(key.scope, key.key) + .toRight(s"Key ${Def displayFull key} not found") + .flatMap { + case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") + case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings") + case x => Right(x) + } + + val values: Either[String, Any] = key match { + case Left(msg) => Left(s"Invalid programmatic input: $msg") + case Right(key) => getSettingValue(key) + } + + val jsonValues: String = values match { + case Left(errors) => errors + case Right(value) => value.toString + } + + SettingQueryResponse(jsonValues) + } } From aa69d0d042469188e1736d8aa16185b5537fd758 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Mar 2017 13:19:01 +0000 Subject: [PATCH 04/19] Split setting query things out into SettingQuery.scala --- .../sbt/internal/server/NetworkChannel.scala | 93 ---------------- .../sbt/internal/server/SettingQuery.scala | 102 ++++++++++++++++++ 2 files changed, 102 insertions(+), 93 deletions(-) create mode 100644 main/src/main/scala/sbt/internal/server/SettingQuery.scala diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 72e3ce2cc..4c51531f9 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -7,7 +7,6 @@ package server import java.net.{ Socket, SocketTimeoutException, URI } import java.util.concurrent.atomic.AtomicBoolean -import scala.util.{ Left, Right } import sbt.protocol._ import sjsonnew._ @@ -88,95 +87,3 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil out.close() } } - -object SettingQuery { - import sbt.internal.util.{ AttributeKey, Settings } - import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._ - import sbt.Def.{ showBuildRelativeKey, ScopedKey } - - // Similar to Act.ParsedAxis / Act.projectRef / Act.resolveProject except you can't omit the project reference - - sealed trait ParsedExplicitAxis[+T] - final object ParsedExplicitGlobal extends ParsedExplicitAxis[Nothing] - final class ParsedExplicitValue[T](val value: T) extends ParsedExplicitAxis[T] - def explicitValue[T](t: Parser[T]): Parser[ParsedExplicitAxis[T]] = t map { v => new ParsedExplicitValue(v) } - - def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedExplicitAxis[ResolvedReference]] = { - val global = token(Act.GlobalString ~ '/') ^^^ ParsedExplicitGlobal - val trailing = '/' !!! "Expected '/' (if selecting a project)" - global | explicitValue(Act.resolvedReference(index, currentBuild, trailing)) - } - - def resolveProject(parsed: ParsedExplicitAxis[ResolvedReference]): Option[ResolvedReference] = parsed match { - case ParsedExplicitGlobal => None - case pv: ParsedExplicitValue[_] => Some(pv.value) - } - - def scopedKeyFull( - index: KeyIndex, - currentBuild: URI, - defaultConfigs: Option[ResolvedReference] => Seq[String], - keyMap: Map[String, AttributeKey[_]] - ): Parser[Seq[Parser[ParsedKey]]] = { - for { - rawProject <- projectRef(index, currentBuild) - proj = resolveProject(rawProject) - confAmb <- Act.config(index configs proj) - partialMask = ScopeMask(true, confAmb.isExplicit, false, false) - } yield Act.taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask) - } - - def scopedKeySelected( - index: KeyIndex, - currentBuild: URI, - defaultConfigs: Option[ResolvedReference] => Seq[String], - keyMap: Map[String, AttributeKey[_]], - data: Settings[Scope] - ): Parser[ParsedKey] = - scopedKeyFull(index, currentBuild, defaultConfigs, keyMap) flatMap { choices => - Act.select(choices, data)(showBuildRelativeKey(currentBuild, index.buildURIs.size > 1)) - } - - def scopedKey( - index: KeyIndex, - currentBuild: URI, - defaultConfigs: Option[ResolvedReference] => Seq[String], - keyMap: Map[String, AttributeKey[_]], - data: Settings[Scope] - ): Parser[ScopedKey[_]] = - scopedKeySelected(index, currentBuild, defaultConfigs, keyMap, data).map(_.key) - - def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] = - scopedKey( - structure.index.keyIndex, - currentBuild, - structure.extra.configurationsForAxis, - structure.index.keyMap, - structure.data - ) - - def handleSettingQuery(req: SettingQuery, structure: BuildStructure, currentBuild: URI): SettingQueryResponse = { - val key = Parser.parse(req.setting, scopedKeyParser(structure, currentBuild)) - - def getSettingValue[A](key: Def.ScopedKey[A]): Either[String, A] = - structure.data.get(key.scope, key.key) - .toRight(s"Key ${Def displayFull key} not found") - .flatMap { - case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") - case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings") - case x => Right(x) - } - - val values: Either[String, Any] = key match { - case Left(msg) => Left(s"Invalid programmatic input: $msg") - case Right(key) => getSettingValue(key) - } - - val jsonValues: String = values match { - case Left(errors) => errors - case Right(value) => value.toString - } - - SettingQueryResponse(jsonValues) - } -} diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala new file mode 100644 index 000000000..196350b18 --- /dev/null +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016-2017 Lightbend Inc. + */ +package sbt +package internal +package server + +import java.net.URI +import scala.util.{ Left, Right } +import sbt.protocol._ + +object SettingQuery { + import sbt.internal.util.{ AttributeKey, Settings } + import sbt.internal.util.complete.{ DefaultParsers, Parser }, DefaultParsers._ + import sbt.Def.{ showBuildRelativeKey, ScopedKey } + + // Similar to Act.ParsedAxis / Act.projectRef / Act.resolveProject except you can't omit the project reference + + sealed trait ParsedExplicitAxis[+T] + final object ParsedExplicitGlobal extends ParsedExplicitAxis[Nothing] + final class ParsedExplicitValue[T](val value: T) extends ParsedExplicitAxis[T] + def explicitValue[T](t: Parser[T]): Parser[ParsedExplicitAxis[T]] = t map { v => new ParsedExplicitValue(v) } + + def projectRef(index: KeyIndex, currentBuild: URI): Parser[ParsedExplicitAxis[ResolvedReference]] = { + val global = token(Act.GlobalString ~ '/') ^^^ ParsedExplicitGlobal + val trailing = '/' !!! "Expected '/' (if selecting a project)" + global | explicitValue(Act.resolvedReference(index, currentBuild, trailing)) + } + + def resolveProject(parsed: ParsedExplicitAxis[ResolvedReference]): Option[ResolvedReference] = parsed match { + case ParsedExplicitGlobal => None + case pv: ParsedExplicitValue[_] => Some(pv.value) + } + + def scopedKeyFull( + index: KeyIndex, + currentBuild: URI, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]] + ): Parser[Seq[Parser[ParsedKey]]] = { + for { + rawProject <- projectRef(index, currentBuild) + proj = resolveProject(rawProject) + confAmb <- Act.config(index configs proj) + partialMask = ScopeMask(true, confAmb.isExplicit, false, false) + } yield Act.taskKeyExtra(index, defaultConfigs, keyMap, proj, confAmb, partialMask) + } + + def scopedKeySelected( + index: KeyIndex, + currentBuild: URI, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]], + data: Settings[Scope] + ): Parser[ParsedKey] = + scopedKeyFull(index, currentBuild, defaultConfigs, keyMap) flatMap { choices => + Act.select(choices, data)(showBuildRelativeKey(currentBuild, index.buildURIs.size > 1)) + } + + def scopedKey( + index: KeyIndex, + currentBuild: URI, + defaultConfigs: Option[ResolvedReference] => Seq[String], + keyMap: Map[String, AttributeKey[_]], + data: Settings[Scope] + ): Parser[ScopedKey[_]] = + scopedKeySelected(index, currentBuild, defaultConfigs, keyMap, data).map(_.key) + + def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] = + scopedKey( + structure.index.keyIndex, + currentBuild, + structure.extra.configurationsForAxis, + structure.index.keyMap, + structure.data + ) + + def handleSettingQuery(req: SettingQuery, structure: BuildStructure, currentBuild: URI): SettingQueryResponse = { + val key = Parser.parse(req.setting, scopedKeyParser(structure, currentBuild)) + + def getSettingValue[A](key: Def.ScopedKey[A]): Either[String, A] = + structure.data.get(key.scope, key.key) + .toRight(s"Key ${Def displayFull key} not found") + .flatMap { + case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") + case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings") + case x => Right(x) + } + + val values: Either[String, Any] = key match { + case Left(msg) => Left(s"Invalid programmatic input: $msg") + case Right(key) => getSettingValue(key) + } + + val jsonValues: String = values match { + case Left(errors) => errors + case Right(value) => value.toString + } + + SettingQueryResponse(jsonValues) + } +} From 992b35af3c84762a33e1c319eee26d50cad1d945 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Mar 2017 13:20:59 +0000 Subject: [PATCH 05/19] Start setup for JSON-format setting values --- .../sbt/internal/server/SettingQuery.scala | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index 196350b18..a7936110f 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -75,28 +75,25 @@ object SettingQuery { structure.data ) + def getSettingValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, A] = + structure.data.get(key.scope, key.key) + .toRight(s"Key ${Def displayFull key} not found") + .flatMap { + case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") + case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings") + case x => Right(x) + } + + def toJsonString[A: Manifest](x: A): String = x.toString + + def getSettingJsonStringValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, String] = + getSettingValue(structure, key) map (toJsonString(_)(key.key.manifest)) + def handleSettingQuery(req: SettingQuery, structure: BuildStructure, currentBuild: URI): SettingQueryResponse = { val key = Parser.parse(req.setting, scopedKeyParser(structure, currentBuild)) - def getSettingValue[A](key: Def.ScopedKey[A]): Either[String, A] = - structure.data.get(key.scope, key.key) - .toRight(s"Key ${Def displayFull key} not found") - .flatMap { - case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings") - case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings") - case x => Right(x) - } + val result: Either[String, String] = key flatMap (getSettingJsonStringValue(structure, _)) - val values: Either[String, Any] = key match { - case Left(msg) => Left(s"Invalid programmatic input: $msg") - case Right(key) => getSettingValue(key) - } - - val jsonValues: String = values match { - case Left(errors) => errors - case Right(value) => value.toString - } - - SettingQueryResponse(jsonValues) + SettingQueryResponse(result.merge) } } From 371f14d9a579de8f726b52b93c9e028146809a37 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Mar 2017 13:55:27 +0000 Subject: [PATCH 06/19] BuildStructure#root is SessionSettings#currentBuild --- main/src/main/scala/sbt/internal/CommandExchange.scala | 2 +- .../main/scala/sbt/internal/server/NetworkChannel.scala | 6 +++--- .../src/main/scala/sbt/internal/server/SettingQuery.scala | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 39697fdc3..d9ed0f40c 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -74,7 +74,7 @@ private[sbt] final class CommandExchange { def onIncomingSocket(socket: Socket): Unit = { s.log.info(s"new client connected from: ${socket.getPort}") - val channel = new NetworkChannel(newChannelName, socket, Project structure s, Project.session(s).currentBuild) + val channel = new NetworkChannel(newChannelName, socket, Project structure s) subscribe(channel) channel.publishEventMessage(ChannelAcceptedEvent(channel.name)) } diff --git a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala index 4c51531f9..5113b8d2b 100644 --- a/main/src/main/scala/sbt/internal/server/NetworkChannel.scala +++ b/main/src/main/scala/sbt/internal/server/NetworkChannel.scala @@ -5,12 +5,12 @@ package sbt package internal package server -import java.net.{ Socket, SocketTimeoutException, URI } +import java.net.{ Socket, SocketTimeoutException } import java.util.concurrent.atomic.AtomicBoolean import sbt.protocol._ import sjsonnew._ -final class NetworkChannel(val name: String, connection: Socket, structure: BuildStructure, currentBuild: URI) extends CommandChannel { +final class NetworkChannel(val name: String, connection: Socket, structure: BuildStructure) extends CommandChannel { private val running = new AtomicBoolean(true) private val delimiter: Byte = '\n'.toByte private val out = connection.getOutputStream @@ -79,7 +79,7 @@ final class NetworkChannel(val name: String, connection: Socket, structure: Buil append(Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) private def onSettingQuery(req: SettingQuery) = - StandardMain.exchange publishEventMessage SettingQuery.handleSettingQuery(req, structure, currentBuild) + StandardMain.exchange publishEventMessage SettingQuery.handleSettingQuery(req, structure) def shutdown(): Unit = { println("Shutting down client connection") diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index a7936110f..c14f78447 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -66,10 +66,10 @@ object SettingQuery { ): Parser[ScopedKey[_]] = scopedKeySelected(index, currentBuild, defaultConfigs, keyMap, data).map(_.key) - def scopedKeyParser(structure: BuildStructure, currentBuild: URI): Parser[ScopedKey[_]] = + def scopedKeyParser(structure: BuildStructure): Parser[ScopedKey[_]] = scopedKey( structure.index.keyIndex, - currentBuild, + structure.root, structure.extra.configurationsForAxis, structure.index.keyMap, structure.data @@ -89,8 +89,8 @@ object SettingQuery { def getSettingJsonStringValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, String] = getSettingValue(structure, key) map (toJsonString(_)(key.key.manifest)) - def handleSettingQuery(req: SettingQuery, structure: BuildStructure, currentBuild: URI): SettingQueryResponse = { - val key = Parser.parse(req.setting, scopedKeyParser(structure, currentBuild)) + def handleSettingQuery(req: SettingQuery, structure: BuildStructure): SettingQueryResponse = { + val key = Parser.parse(req.setting, scopedKeyParser(structure)) val result: Either[String, String] = key flatMap (getSettingJsonStringValue(structure, _)) From 6e6f66d6d01618c4091465981a504ef377d0d7a7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Mar 2017 14:37:44 +0000 Subject: [PATCH 07/19] Start structure to discover JsonWriter --- .../scala/sbt/internal/server/SettingQuery.scala | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index c14f78447..d9d2eb8ed 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -8,6 +8,8 @@ package server import java.net.URI import scala.util.{ Left, Right } import sbt.protocol._ +import sjsonnew._ +import sjsonnew.support.scalajson.unsafe._ object SettingQuery { import sbt.internal.util.{ AttributeKey, Settings } @@ -84,7 +86,18 @@ object SettingQuery { case x => Right(x) } - def toJsonString[A: Manifest](x: A): String = x.toString + def getJsonWriter[A: Manifest](x: A): Option[JsonWriter[A]] = None + + def toJsonStringStrict[A: Manifest](x: A): Either[String, String] = + getJsonWriter[A](x) + .toRight(s"JsonWriter for ${manifest[A]} not found") + .map(implicit jsonWriter => CompactPrinter(Converter.toJsonUnsafe(x))) + + def toJsonString[A: Manifest](x: A): String = + toJsonStringStrict(x) match { + case Right(s) => s + case Left(_) => x.toString + } def getSettingJsonStringValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, String] = getSettingValue(structure, key) map (toJsonString(_)(key.key.manifest)) From ede2b865b40b5077448d3606a4fd42c2b9d571d0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Mar 2017 10:58:18 +0000 Subject: [PATCH 08/19] Allow disablePlugins(SbtScalariform) to work SbtScalariform is now an AutoPlugin, so its scalariformSettings are already added to the project. So don't add it again otherwise `disablePlugins(SbtScalariform)` won't work. And miscellaneous cleanups. --- project/Formatting.scala | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/project/Formatting.scala b/project/Formatting.scala index a760af123..30259e009 100644 --- a/project/Formatting.scala +++ b/project/Formatting.scala @@ -1,15 +1,13 @@ -import sbt._ -import sbt.Keys._ -import com.typesafe.sbt.SbtScalariform._ -import ScalariformKeys.{ format => scalariformFormat, preferences => scalariformPreferences } +import sbt._, Keys._ +import com.typesafe.sbt.SbtScalariform._, autoImport._ object Formatting { - lazy val BuildConfig = config("build") extend Compile - lazy val BuildSbtConfig = config("buildsbt") extend Compile + val BuildConfig = config("build") extend Compile + val BuildSbtConfig = config("buildsbt") extend Compile val scalariformCheck = taskKey[Unit]("Checks that the existing code is formatted, via git diff") - lazy val prefs: Seq[Setting[_]] = { + private val prefs: Seq[Setting[_]] = { import scalariform.formatter.preferences._ Seq( scalariformPreferences ~= (_ @@ -20,8 +18,8 @@ object Formatting { ) } - lazy val settings: Seq[Setting[_]] = Seq() ++ scalariformSettings ++ prefs - lazy val sbtFilesSettings: Seq[Setting[_]] = Seq() ++ scalariformSettings ++ prefs ++ + val settings: Seq[Setting[_]] = Seq() ++ prefs + val sbtFilesSettings: Seq[Setting[_]] = Seq() ++ prefs ++ inConfig(BuildConfig)(configScalariformSettings) ++ inConfig(BuildSbtConfig)(configScalariformSettings) ++ Seq( From 342fc2cf8fb17104f5208a0094dd9f829d7fe66a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Mar 2017 12:43:27 +0000 Subject: [PATCH 09/19] Commit to the current JsonFormatRegistry. --- .../internal/server/JsonFormatRegistry.scala | 160 ++++++++++++++++++ .../sbt/internal/server/SettingQuery.scala | 2 +- 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala diff --git a/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala b/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala new file mode 100644 index 000000000..1d8995454 --- /dev/null +++ b/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2017 Lightbend Inc. + */ +package sbt +package internal +package server + +import java.io.File +import java.net.{ URI, URL } +import scala.{ collection => sc }, sc.{ immutable => sci }, sci.{ Seq => sciSeq } +import scala.util.{ Left, Right } +import sbt.librarymanagement.LibraryManagementCodec._ +import sjsonnew._ + +/** A registry of JsonFormat instances. + * + * Used to lookup, given a value of type 'A', its 'JsonFormat[A]' instance. + */ +object JsonFormatRegistry { + type JF[A] = JsonFormat[A] + type CTag[A] = scala.reflect.ClassTag[A] + + @inline def ?[A](implicit z: A): A = z + @inline def classTag[A: CTag]: CTag[A] = ? + + val UnitJF: JF[Unit] = ? + val BooleanJF: JF[Boolean] = ? + val ByteJF: JF[Byte] = ? + val ShortJF: JF[Short] = ? + val CharJF: JF[Char] = ? + val IntJF: JF[Int] = ? + val LongJF: JF[Int] = ? + val FloatJF: JF[Float] = ? + val DoubleJF: JF[Double] = ? + val StringJF: JF[String] = ? + val SymbolJF: JF[Symbol] = ? + val FileJF: JF[File] = ? + val URIJF: JF[URI] = ? + val URLJF: JF[URL] = ? + + def cast[A](z: JsonFormat[_]): JsonFormat[A] = z.asInstanceOf[JsonFormat[A]] + def castAndWrap[A](z: JsonFormat[_]): Option[JsonFormat[A]] = Some(cast(z)) + + def optionJF[A](x: Option[A]): Option[JF[Option[A]]] = x match { + case None => castAndWrap(?[JF[Option[Int]]]) + case Some(x) => lookup(x) map (implicit elemJF => ?) + } + + def eitherJF[A, B](x: Either[A, B]): Option[JF[Either[A, B]]] = x match { + case Left(x) => lookup(x) map { implicit lJF: JF[A] => + implicit val rJF: JF[B] = cast(UnitJF) + ?[JF[Either[A, B]]] + } + case Right(x) => lookup(x) map { implicit rJF: JF[B] => + implicit val lJF: JF[A] = cast(UnitJF) + ?[JF[Either[A, B]]] + } + } + + def tuple1JF[A](x: Tuple1[A]): Option[JF[Tuple1[A]]] = lookup(x._1) map { implicit elemJF: JF[A] => ? } + + def tuple2JF[A, B](x: (A, B)): Option[JF[(A, B)]] = + for (aJF <- lookup(x._1); bJF <- lookup(x._2)) yield { + implicit val aJFi: JF[A] = aJF + implicit val bJFi: JF[B] = bJF + ? + } + + def tuple3JF[A, B, C](x: (A, B, C)): Option[JF[(A, B, C)]] = + for (aJF <- lookup(x._1); bJF <- lookup(x._2); cJF <- lookup(x._3)) yield { + implicit val aJFi: JF[A] = aJF + implicit val bJFi: JF[B] = bJF + implicit val cJFi: JF[C] = cJF + ? + } + + def tuple4JF[A, B, C, D](x: (A, B, C, D)): Option[JF[(A, B, C, D)]] = + for (aJF <- lookup(x._1); bJF <- lookup(x._2); cJF <- lookup(x._3); dJF <- lookup(x._4)) yield { + implicit val aJFi: JF[A] = aJF + implicit val bJFi: JF[B] = bJF + implicit val cJFi: JF[C] = cJF + implicit val dJFi: JF[D] = dJF + ? + } + + def listJF[A](x: List[A]): Option[JF[List[A]]] = x match { + case Nil => castAndWrap(?[JF[List[Int]]]) + case x :: _ => lookup(x) map { implicit elemJF: JF[A] => ? } + } + + def arrayJF[A](x: Array[A]): Option[JF[Array[A]]] = x match { + case Array() => castAndWrap(?[JF[Array[Int]]]) + case Array(x, _*) => lookup(x) map { implicit elemJF: JF[A] => + implicit val ctag: CTag[A] = classTag[AnyRef].asInstanceOf[CTag[A]] // if A is a primitive.. boom + ? + } + } + + // Map is a PITA, because it needs a JsonKeyFormat, not the "Key" +// def mapJF[A, B](x: Map[A, B]): Option[JF[Map[A, B]]] = x.headOption match { +// case None => castAndWrap(?[JF[Map[Int, Int]]]) +// case Some((a, b)) => for (aJF <- lookup(a); bJF <- lookup(b)) yield { +// implicit val aJFi: JF[A] = aJF +// implicit val bJFi: JF[B] = bJF +// ?[JF[Map[A, B]]] +// mapFormat[A, B] +// } +// } + + def vectorJF[A](x: Vector[A]): Option[JF[Vector[A]]] = x match { + case Vector() => castAndWrap(?[JF[Vector[Int]]]) + case Vector(x, _*) => lookup(x) map { implicit elemJF: JF[A] => ? } + } + + def sciSeqJF[A](x: sciSeq[A]): Option[JF[sciSeq[A]]] = x match { + case sci.Seq() => castAndWrap(?[JF[sciSeq[Int]]]) + case sci.Seq(x, _*) => lookup(x) map { implicit elemJF: JF[A] => ? } + } + + def seqJF[A](x: Seq[A]): Option[JF[Seq[A]]] = x match { + case Seq() => castAndWrap(?[JF[Seq[Int]]]) + case Seq(x, _*) => lookup(x) map { implicit elemJF: JF[A] => ? } + } + + // TODO: Upgrade sjsonnew version +// def optional[A](x: Optional[A]): Option[JF[Optional[A]]] = +// if (x.isPresent) lookup(x.get) map { implicit elemJF: JF[A] => ? } +// else castAndWrap(?[JF[Optional[Int]]]) + + def lookup[A](value: A): Option[JsonFormat[A]] = value match { + case _: Unit => castAndWrap[A]( UnitJF) + case _: Boolean => castAndWrap[A](BooleanJF) + case _: Byte => castAndWrap[A]( ByteJF) + case _: Short => castAndWrap[A]( ShortJF) + case _: Char => castAndWrap[A]( CharJF) + case _: Int => castAndWrap[A]( IntJF) + case _: Long => castAndWrap[A]( LongJF) + case _: Float => castAndWrap[A]( FloatJF) + case _: Double => castAndWrap[A]( DoubleJF) + case _: String => castAndWrap[A]( StringJF) + case _: Symbol => castAndWrap[A]( SymbolJF) + case _: File => castAndWrap[A]( FileJF) + case _: URI => castAndWrap[A]( URIJF) + case _: URL => castAndWrap[A]( URLJF) + case x: Option[_] => optionJF(x) map cast + case x: Either[_, _] => eitherJF(x) map cast + case x: Tuple1[_] => tuple1JF(x) map cast + case x: (_, _) => tuple2JF(x) map cast + case x: (_, _, _) => tuple3JF(x) map cast + case x: (_, _, _, _) => tuple4JF(x) map cast + case x: List[_] => listJF(x) map cast + case x: Array[_] => arrayJF(x) map cast +// case x: Map[_, _] => mapJF(x) map cast + case x: Vector[_] => vectorJF(x) map cast + case x: sciSeq[_] => sciSeqJF(x) map cast + case x: Seq[_] => seqJF(x) map cast +// case x: Optional[_] => optionalJF(x) map cast + case _ => None + } +} diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index d9d2eb8ed..a0dc31829 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -86,7 +86,7 @@ object SettingQuery { case x => Right(x) } - def getJsonWriter[A: Manifest](x: A): Option[JsonWriter[A]] = None + def getJsonWriter[A: Manifest](x: A): Option[JsonWriter[A]] = JsonFormatRegistry lookup x def toJsonStringStrict[A: Manifest](x: A): Either[String, String] = getJsonWriter[A](x) From f2b70de5384413834a3381bbad442163e26aff27 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Mar 2017 13:59:30 +0000 Subject: [PATCH 10/19] Implement a Manifest-based lookup --- .../internal/server/JsonFormatRegistry.scala | 149 +++++++----------- .../sbt/internal/server/SettingQuery.scala | 4 +- 2 files changed, 58 insertions(+), 95 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala b/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala index 1d8995454..3bdf1f894 100644 --- a/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala +++ b/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala @@ -8,7 +8,6 @@ package server import java.io.File import java.net.{ URI, URL } import scala.{ collection => sc }, sc.{ immutable => sci }, sci.{ Seq => sciSeq } -import scala.util.{ Left, Right } import sbt.librarymanagement.LibraryManagementCodec._ import sjsonnew._ @@ -17,6 +16,7 @@ import sjsonnew._ * Used to lookup, given a value of type 'A', its 'JsonFormat[A]' instance. */ object JsonFormatRegistry { + type MF[A] = Manifest[A] type JF[A] = JsonFormat[A] type CTag[A] = scala.reflect.ClassTag[A] @@ -41,41 +41,41 @@ object JsonFormatRegistry { def cast[A](z: JsonFormat[_]): JsonFormat[A] = z.asInstanceOf[JsonFormat[A]] def castAndWrap[A](z: JsonFormat[_]): Option[JsonFormat[A]] = Some(cast(z)) - def optionJF[A](x: Option[A]): Option[JF[Option[A]]] = x match { - case None => castAndWrap(?[JF[Option[Int]]]) - case Some(x) => lookup(x) map (implicit elemJF => ?) - } + // TODO: Any way to de-duplify here? + def tuple1JF[A: Manifest]: Option[JF[ Tuple1[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } + def optionJF[A: Manifest]: Option[JF[ Option[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } + def listJF[A: Manifest]: Option[JF[ List[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } + def arrayJF[A: Manifest]: Option[JF[ Array[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } + def vectorJF[A: Manifest]: Option[JF[ Vector[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } + def sciSeqJF[A: Manifest]: Option[JF[ sciSeq[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } + def seqJF[A: Manifest]: Option[JF[ Seq[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } +//def optionalJF[A: Manifest]: Option[JF[Optional[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } // TODO: Upgrade sjsonnew version - def eitherJF[A, B](x: Either[A, B]): Option[JF[Either[A, B]]] = x match { - case Left(x) => lookup(x) map { implicit lJF: JF[A] => - implicit val rJF: JF[B] = cast(UnitJF) - ?[JF[Either[A, B]]] - } - case Right(x) => lookup(x) map { implicit rJF: JF[B] => - implicit val lJF: JF[A] = cast(UnitJF) - ?[JF[Either[A, B]]] - } - } - - def tuple1JF[A](x: Tuple1[A]): Option[JF[Tuple1[A]]] = lookup(x._1) map { implicit elemJF: JF[A] => ? } - - def tuple2JF[A, B](x: (A, B)): Option[JF[(A, B)]] = - for (aJF <- lookup(x._1); bJF <- lookup(x._2)) yield { + def eitherJF[A: Manifest, B: Manifest]: Option[JF[Either[A, B]]] = + for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B])) yield { implicit val aJFi: JF[A] = aJF implicit val bJFi: JF[B] = bJF ? } - def tuple3JF[A, B, C](x: (A, B, C)): Option[JF[(A, B, C)]] = - for (aJF <- lookup(x._1); bJF <- lookup(x._2); cJF <- lookup(x._3)) yield { + + def tuple2JF[A: Manifest, B: Manifest]: Option[JF[(A, B)]] = + for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B])) yield { + implicit val aJFi: JF[A] = aJF + implicit val bJFi: JF[B] = bJF + ? + } + + def tuple3JF[A: Manifest, B: Manifest, C: Manifest]: Option[JF[(A, B, C)]] = + for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B]); cJF <- lookup(manifest[C])) yield { implicit val aJFi: JF[A] = aJF implicit val bJFi: JF[B] = bJF implicit val cJFi: JF[C] = cJF ? } - def tuple4JF[A, B, C, D](x: (A, B, C, D)): Option[JF[(A, B, C, D)]] = - for (aJF <- lookup(x._1); bJF <- lookup(x._2); cJF <- lookup(x._3); dJF <- lookup(x._4)) yield { + def tuple4JF[A: Manifest, B: Manifest, C: Manifest, D: Manifest]: Option[JF[(A, B, C, D)]] = + for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B]); cJF <- lookup(manifest[C]); dJF <- lookup(manifest[D])) yield { implicit val aJFi: JF[A] = aJF implicit val bJFi: JF[B] = bJF implicit val cJFi: JF[C] = cJF @@ -83,78 +83,43 @@ object JsonFormatRegistry { ? } - def listJF[A](x: List[A]): Option[JF[List[A]]] = x match { - case Nil => castAndWrap(?[JF[List[Int]]]) - case x :: _ => lookup(x) map { implicit elemJF: JF[A] => ? } - } - - def arrayJF[A](x: Array[A]): Option[JF[Array[A]]] = x match { - case Array() => castAndWrap(?[JF[Array[Int]]]) - case Array(x, _*) => lookup(x) map { implicit elemJF: JF[A] => - implicit val ctag: CTag[A] = classTag[AnyRef].asInstanceOf[CTag[A]] // if A is a primitive.. boom - ? - } - } - - // Map is a PITA, because it needs a JsonKeyFormat, not the "Key" -// def mapJF[A, B](x: Map[A, B]): Option[JF[Map[A, B]]] = x.headOption match { -// case None => castAndWrap(?[JF[Map[Int, Int]]]) -// case Some((a, b)) => for (aJF <- lookup(a); bJF <- lookup(b)) yield { + // Map is a PITA, because it needs a JsonKeyFormat, note the "Key" +// def mapJF[A: Manifest, B: Manifest]: Option[JF[Map[A, B]]] = +// for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[A])) yield { // implicit val aJFi: JF[A] = aJF // implicit val bJFi: JF[B] = bJF -// ?[JF[Map[A, B]]] -// mapFormat[A, B] +// ? // } // } - def vectorJF[A](x: Vector[A]): Option[JF[Vector[A]]] = x match { - case Vector() => castAndWrap(?[JF[Vector[Int]]]) - case Vector(x, _*) => lookup(x) map { implicit elemJF: JF[A] => ? } - } - - def sciSeqJF[A](x: sciSeq[A]): Option[JF[sciSeq[A]]] = x match { - case sci.Seq() => castAndWrap(?[JF[sciSeq[Int]]]) - case sci.Seq(x, _*) => lookup(x) map { implicit elemJF: JF[A] => ? } - } - - def seqJF[A](x: Seq[A]): Option[JF[Seq[A]]] = x match { - case Seq() => castAndWrap(?[JF[Seq[Int]]]) - case Seq(x, _*) => lookup(x) map { implicit elemJF: JF[A] => ? } - } - - // TODO: Upgrade sjsonnew version -// def optional[A](x: Optional[A]): Option[JF[Optional[A]]] = -// if (x.isPresent) lookup(x.get) map { implicit elemJF: JF[A] => ? } -// else castAndWrap(?[JF[Optional[Int]]]) - - def lookup[A](value: A): Option[JsonFormat[A]] = value match { - case _: Unit => castAndWrap[A]( UnitJF) - case _: Boolean => castAndWrap[A](BooleanJF) - case _: Byte => castAndWrap[A]( ByteJF) - case _: Short => castAndWrap[A]( ShortJF) - case _: Char => castAndWrap[A]( CharJF) - case _: Int => castAndWrap[A]( IntJF) - case _: Long => castAndWrap[A]( LongJF) - case _: Float => castAndWrap[A]( FloatJF) - case _: Double => castAndWrap[A]( DoubleJF) - case _: String => castAndWrap[A]( StringJF) - case _: Symbol => castAndWrap[A]( SymbolJF) - case _: File => castAndWrap[A]( FileJF) - case _: URI => castAndWrap[A]( URIJF) - case _: URL => castAndWrap[A]( URLJF) - case x: Option[_] => optionJF(x) map cast - case x: Either[_, _] => eitherJF(x) map cast - case x: Tuple1[_] => tuple1JF(x) map cast - case x: (_, _) => tuple2JF(x) map cast - case x: (_, _, _) => tuple3JF(x) map cast - case x: (_, _, _, _) => tuple4JF(x) map cast - case x: List[_] => listJF(x) map cast - case x: Array[_] => arrayJF(x) map cast -// case x: Map[_, _] => mapJF(x) map cast - case x: Vector[_] => vectorJF(x) map cast - case x: sciSeq[_] => sciSeqJF(x) map cast - case x: Seq[_] => seqJF(x) map cast -// case x: Optional[_] => optionalJF(x) map cast - case _ => None + def lookup[A: Manifest]: Option[JsonFormat[A]] = manifest[A] match { + case Manifest.Unit => castAndWrap[A]( UnitJF) + case Manifest.Boolean => castAndWrap[A](BooleanJF) + case Manifest.Byte => castAndWrap[A]( ByteJF) + case Manifest.Short => castAndWrap[A]( ShortJF) + case Manifest.Char => castAndWrap[A]( CharJF) + case Manifest.Int => castAndWrap[A]( IntJF) + case Manifest.Long => castAndWrap[A]( LongJF) + case Manifest.Float => castAndWrap[A]( FloatJF) + case Manifest.Double => castAndWrap[A]( DoubleJF) + case m if m.runtimeClass == classOf[String] => castAndWrap[A]( StringJF) + case m if m.runtimeClass == classOf[Symbol] => castAndWrap[A]( SymbolJF) + case m if m.runtimeClass == classOf[File] => castAndWrap[A]( FileJF) + case m if m.runtimeClass == classOf[URI] => castAndWrap[A]( URIJF) + case m if m.runtimeClass == classOf[URL] => castAndWrap[A]( URLJF) + case m if m.runtimeClass == classOf[Option[_]] => optionJF(m.typeArguments.head) map cast + case m if m.runtimeClass == classOf[Either[_, _]] => eitherJF(m.typeArguments.head, m.typeArguments(1)) map cast + case m if m.runtimeClass == classOf[Tuple1[_]] => tuple1JF(m.typeArguments.head) map cast + case m if m.runtimeClass == classOf[(_, _)] => tuple2JF(m.typeArguments.head, m.typeArguments(1)) map cast + case m if m.runtimeClass == classOf[(_, _, _)] => tuple3JF(m.typeArguments.head, m.typeArguments(1), m.typeArguments(2)) map cast + case m if m.runtimeClass == classOf[(_, _, _, _)] => tuple4JF(m.typeArguments.head, m.typeArguments(1), m.typeArguments(2), m.typeArguments(3)) map cast + case m if m.runtimeClass == classOf[List[_]] => listJF(m.typeArguments.head) map cast + case m if m.runtimeClass == classOf[Array[_]] => arrayJF(m.typeArguments.head) map cast +// case m if m.runtimeClass == classOf[Map[_, _]] => mapJF(m.typeArguments.head, m.typeArguments(1)) map cast + case m if m.runtimeClass == classOf[Vector[_]] => vectorJF(m.typeArguments.head) map cast + case m if m.runtimeClass == classOf[sciSeq[_]] => sciSeqJF(m.typeArguments.head) map cast + case m if m.runtimeClass == classOf[Seq[_]] => seqJF(m.typeArguments.head) map cast +// case m if m.runtimeClass == classOf[Optional[_]] => optionalJF(m.typeArguments.head) map cast + case _ => None } } diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index a0dc31829..84c98232c 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -86,10 +86,8 @@ object SettingQuery { case x => Right(x) } - def getJsonWriter[A: Manifest](x: A): Option[JsonWriter[A]] = JsonFormatRegistry lookup x - def toJsonStringStrict[A: Manifest](x: A): Either[String, String] = - getJsonWriter[A](x) + JsonFormatRegistry.lookup[A] .toRight(s"JsonWriter for ${manifest[A]} not found") .map(implicit jsonWriter => CompactPrinter(Converter.toJsonUnsafe(x))) From 43eec230e66a835d866910b065909a3c9f2ae222 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Mar 2017 16:44:57 +0000 Subject: [PATCH 11/19] Switch SettingQueryResponse to JValue, implement JValueFormat --- build.sbt | 4 +- .../sbt/internal/server/SettingQuery.scala | 26 +++++--- project/ContrabandConfig.scala | 1 + .../sbt/protocol/SettingQueryResponse.scala | 8 +-- .../protocol/codec/EventMessageFormats.scala | 2 +- .../sbt/protocol/codec/JsonProtocol.scala | 1 + .../codec/SettingQueryResponseFormats.scala | 4 +- protocol/src/main/contraband/server.contra | 2 +- .../scala/sbt/internal/JValueFormat.scala | 61 +++++++++++++++++++ 9 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 protocol/src/main/scala/sbt/internal/JValueFormat.scala diff --git a/build.sbt b/build.sbt index b8ff283b6..d4fc0cbab 100644 --- a/build.sbt +++ b/build.sbt @@ -181,7 +181,8 @@ lazy val protocolProj = (project in file("protocol")). testedBaseSettings, name := "Protocol", libraryDependencies ++= Seq(sjsonNewScalaJson), - sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala" + sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", + contrabandFormatsForType in generateContrabands in Compile := ContrabandConfig.getFormats ). configure(addSbtUtilLogging) @@ -211,6 +212,7 @@ lazy val mainSettingsProj = (project in file("main-settings")). // The main integration project for sbt. It brings all of the projects together, configures them, and provides for overriding conventions. lazy val mainProj = (project in file("main")). dependsOn(actionsProj, mainSettingsProj, runProj, commandProj). + disablePlugins(SbtScalariform). settings( testedBaseSettings, name := "Main", diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index 84c98232c..0a290b934 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -6,9 +6,10 @@ package internal package server import java.net.URI +import scala.json.ast.unsafe.JValue import scala.util.{ Left, Right } +import sbt.librarymanagement.LibraryManagementCodec._ import sbt.protocol._ -import sjsonnew._ import sjsonnew.support.scalajson.unsafe._ object SettingQuery { @@ -86,25 +87,30 @@ object SettingQuery { case x => Right(x) } - def toJsonStringStrict[A: Manifest](x: A): Either[String, String] = + def toJsonStringStrict[A: Manifest](x: A): Either[String, JValue] = JsonFormatRegistry.lookup[A] .toRight(s"JsonWriter for ${manifest[A]} not found") - .map(implicit jsonWriter => CompactPrinter(Converter.toJsonUnsafe(x))) + .map(implicit jsonWriter => Converter toJsonUnsafe x) - def toJsonString[A: Manifest](x: A): String = + def toJson[A: Manifest](x: A): JValue = toJsonStringStrict(x) match { - case Right(s) => s - case Left(_) => x.toString + case Right(j) => j + case Left(_) => Converter toJsonUnsafe x.toString } - def getSettingJsonStringValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, String] = - getSettingValue(structure, key) map (toJsonString(_)(key.key.manifest)) + def getSettingJsonStringValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, JValue] = + getSettingValue(structure, key) map (toJson(_)(key.key.manifest)) def handleSettingQuery(req: SettingQuery, structure: BuildStructure): SettingQueryResponse = { val key = Parser.parse(req.setting, scopedKeyParser(structure)) - val result: Either[String, String] = key flatMap (getSettingJsonStringValue(structure, _)) + val result: Either[String, JValue] = key flatMap (getSettingJsonStringValue(structure, _)) - SettingQueryResponse(result.merge) + val finalResult: JValue = result match { + case Right(j) => j + case Left(s) => Converter toJsonUnsafe s + } + + SettingQueryResponse(finalResult) } } diff --git a/project/ContrabandConfig.scala b/project/ContrabandConfig.scala index d57d87afa..7fb8a2b8e 100644 --- a/project/ContrabandConfig.scala +++ b/project/ContrabandConfig.scala @@ -22,6 +22,7 @@ object ContrabandConfig { case "Option" | "Set" | "scala.Vector" => { tpe => getFormats(oneArg(tpe)) } case "Map" | "Tuple2" | "scala.Tuple2" => { tpe => twoArgs(tpe).flatMap(getFormats) } case "Int" | "Long" => { _ => Nil } + case "scala.json.ast.unsafe.JValue" => { _ => "sbt.internal.JValueFormat" :: Nil } } /** Returns the list of formats required to encode the given `TpeRef`. */ diff --git a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala index 0c7878968..633801200 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala @@ -5,7 +5,7 @@ // DO NOT EDIT MANUALLY package sbt.protocol final class SettingQueryResponse private ( - val value: String) extends sbt.protocol.EventMessage() with Serializable { + val value: scala.json.ast.unsafe.JValue) extends sbt.protocol.EventMessage() with Serializable { @@ -19,14 +19,14 @@ final class SettingQueryResponse private ( override def toString: String = { "SettingQueryResponse(" + value + ")" } - protected[this] def copy(value: String = value): SettingQueryResponse = { + protected[this] def copy(value: scala.json.ast.unsafe.JValue = value): SettingQueryResponse = { new SettingQueryResponse(value) } - def withValue(value: String): SettingQueryResponse = { + def withValue(value: scala.json.ast.unsafe.JValue): SettingQueryResponse = { copy(value = value) } } object SettingQueryResponse { - def apply(value: String): SettingQueryResponse = new SettingQueryResponse(value) + def apply(value: scala.json.ast.unsafe.JValue): SettingQueryResponse = new SettingQueryResponse(value) } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala index 41ffd6dc9..d74e27dce 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala @@ -5,6 +5,6 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.protocol.codec.SettingQueryResponseFormats => +trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.JValueFormat with sbt.protocol.codec.SettingQueryResponseFormats => implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat4[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQueryResponse]("type") } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala index e2a840a52..e91f6606c 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala @@ -11,6 +11,7 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats + with sbt.internal.JValueFormat with sbt.protocol.codec.SettingQueryResponseFormats with sbt.protocol.codec.EventMessageFormats with sbt.protocol.codec.ExecutionEventFormats diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala index ce7f7a217..45d0756c8 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala @@ -5,13 +5,13 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait SettingQueryResponseFormats { self: sjsonnew.BasicJsonProtocol => +trait SettingQueryResponseFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol => implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = new JsonFormat[sbt.protocol.SettingQueryResponse] { override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQueryResponse = { jsOpt match { case Some(js) => unbuilder.beginObject(js) - val value = unbuilder.readField[String]("value") + val value = unbuilder.readField[scala.json.ast.unsafe.JValue]("value") unbuilder.endObject() sbt.protocol.SettingQueryResponse(value) case None => diff --git a/protocol/src/main/contraband/server.contra b/protocol/src/main/contraband/server.contra index 44103c0c1..7d9798aa8 100644 --- a/protocol/src/main/contraband/server.contra +++ b/protocol/src/main/contraband/server.contra @@ -41,7 +41,7 @@ type ExecStatusEvent implements EventMessage { } type SettingQueryResponse implements EventMessage { - value: String! + value: scala.json.ast.unsafe.JValue! } # enum Status { diff --git a/protocol/src/main/scala/sbt/internal/JValueFormat.scala b/protocol/src/main/scala/sbt/internal/JValueFormat.scala new file mode 100644 index 000000000..e74d3bc73 --- /dev/null +++ b/protocol/src/main/scala/sbt/internal/JValueFormat.scala @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 Lightbend Inc. + */ +package sbt +package internal + +import sjsonnew.{ JsonWriter => JW, JsonReader => JR, JsonFormat => JF, _ } +import scala.json.ast.unsafe._ + +trait JValueFormat { self: sjsonnew.BasicJsonProtocol => + /** Define a JsonWriter for the type T wrapper of underlying type U given JsonWriter[U] and T => U. */ + def unlift[T, U](unlift: T => U)(implicit z: JW[U]): JW[T] = new JW[T] { + def write[J](w: T, b: Builder[J]) = z.write(unlift(w), b) + } + + /** Define a JsonReader for the type T wrapper of underlying type U given JsonReader[U] and U => T. */ + def lift[T, U](lift: U => T)(implicit z: JR[U]): JR[T] = new JR[T] { + def read[J](j: Option[J], u: Unbuilder[J]): T = lift(z.read(j, u)) + } + + @inline def ?[A](implicit z: A): A = z + + implicit val JNullJW: JW[JNull.type] = new JW[JNull.type] { def write[J](x: JNull.type, b: Builder[J]) = b.writeNull() } + implicit val JNullJR: JR[JNull.type] = new JR[JNull.type] { def read[J](j: Option[J], u: Unbuilder[J]) = JNull } + + implicit val JBooleanJW: JW[JBoolean] = unlift(_.get) + implicit val JBooleanJR: JR[JBoolean] = lift(JBoolean(_)) + + implicit val JStringJW: JW[JString] = unlift(_.value) + implicit val JStringJR: JR[JString] = lift(JString(_)) + + implicit val JNumberJW: JW[JNumber] = unlift(x => BigDecimal(x.value)) + implicit val JNumberJR: JR[JNumber] = lift((x: BigDecimal) => JNumber(x.toString)) + + implicit lazy val JArrayJW: JW[JArray] = unlift[JArray, Array[JValue]](_.value) + + implicit lazy val JObjectJW: JW[JObject] = new JW[JObject] { + def write[J](x: JObject, b: Builder[J]) = { + b.beginObject() + x.value foreach (jsonField => JValueJW.addField(jsonField.field, jsonField.value, b)) + b.endObject() + } + } + + implicit lazy val JValueJW: JW[JValue] = new JW[JValue] { + def write[J](x: JValue, b: Builder[J]) = x match { + case x: JNull.type => ?[JW[JNull.type]].write(x, b) + case x: JBoolean => ?[JW[JBoolean]].write(x, b) + case x: JString => ?[JW[JString]].write(x, b) + case x: JNumber => ?[JW[JNumber]].write(x, b) + case x: JObject => ?[JW[JObject]].write(x, b) + case x: JArray => ?[JW[JArray]].write(x, b) + } + } + + implicit lazy val JValueJR: JR[JValue] = new JR[JValue] { + def read[J](j: Option[J], u: Unbuilder[J]) = ??? // Is this even possible? with no Manifest[J]? + } + + implicit lazy val JValueJF: JF[JValue] = jsonFormat[JValue](JValueJR, JValueJW) +} From 6211e8d7dae89fc26debf2db32a09f9132943acd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Mar 2017 17:03:07 +0000 Subject: [PATCH 12/19] Move some things from terms to imports --- .../sbt/internal/server/JsonFormatRegistry.scala | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala b/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala index 3bdf1f894..5a6daa5fa 100644 --- a/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala +++ b/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala @@ -9,19 +9,14 @@ import java.io.File import java.net.{ URI, URL } import scala.{ collection => sc }, sc.{ immutable => sci }, sci.{ Seq => sciSeq } import sbt.librarymanagement.LibraryManagementCodec._ -import sjsonnew._ +import sjsonnew.{ JsonFormat => JF } /** A registry of JsonFormat instances. * * Used to lookup, given a value of type 'A', its 'JsonFormat[A]' instance. */ object JsonFormatRegistry { - type MF[A] = Manifest[A] - type JF[A] = JsonFormat[A] - type CTag[A] = scala.reflect.ClassTag[A] - - @inline def ?[A](implicit z: A): A = z - @inline def classTag[A: CTag]: CTag[A] = ? + @inline def ?[A](implicit z: A): A = z val UnitJF: JF[Unit] = ? val BooleanJF: JF[Boolean] = ? @@ -38,8 +33,8 @@ object JsonFormatRegistry { val URIJF: JF[URI] = ? val URLJF: JF[URL] = ? - def cast[A](z: JsonFormat[_]): JsonFormat[A] = z.asInstanceOf[JsonFormat[A]] - def castAndWrap[A](z: JsonFormat[_]): Option[JsonFormat[A]] = Some(cast(z)) + def cast[A](z: JF[_]): JF[A] = z.asInstanceOf[JF[A]] + def castAndWrap[A](z: JF[_]): Option[JF[A]] = Some(cast(z)) // TODO: Any way to de-duplify here? def tuple1JF[A: Manifest]: Option[JF[ Tuple1[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } @@ -92,7 +87,7 @@ object JsonFormatRegistry { // } // } - def lookup[A: Manifest]: Option[JsonFormat[A]] = manifest[A] match { + def lookup[A: Manifest]: Option[JF[A]] = manifest[A] match { case Manifest.Unit => castAndWrap[A]( UnitJF) case Manifest.Boolean => castAndWrap[A](BooleanJF) case Manifest.Byte => castAndWrap[A]( ByteJF) From 875cf6f4dc9fe510583b6a222be3b13e2a20f23f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Mar 2017 17:10:05 +0000 Subject: [PATCH 13/19] Simplify JValueFormat --- .../scala/sbt/internal/JValueFormat.scala | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/protocol/src/main/scala/sbt/internal/JValueFormat.scala b/protocol/src/main/scala/sbt/internal/JValueFormat.scala index e74d3bc73..d6f2a6dc9 100644 --- a/protocol/src/main/scala/sbt/internal/JValueFormat.scala +++ b/protocol/src/main/scala/sbt/internal/JValueFormat.scala @@ -8,31 +8,15 @@ import sjsonnew.{ JsonWriter => JW, JsonReader => JR, JsonFormat => JF, _ } import scala.json.ast.unsafe._ trait JValueFormat { self: sjsonnew.BasicJsonProtocol => - /** Define a JsonWriter for the type T wrapper of underlying type U given JsonWriter[U] and T => U. */ - def unlift[T, U](unlift: T => U)(implicit z: JW[U]): JW[T] = new JW[T] { - def write[J](w: T, b: Builder[J]) = z.write(unlift(w), b) + implicit val JNullJF: JF[JNull.type] = new JF[JNull.type] { + def write[J](x: JNull.type, b: Builder[J]) = b.writeNull() + def read[J](j: Option[J], u: Unbuilder[J]) = JNull } - /** Define a JsonReader for the type T wrapper of underlying type U given JsonReader[U] and U => T. */ - def lift[T, U](lift: U => T)(implicit z: JR[U]): JR[T] = new JR[T] { - def read[J](j: Option[J], u: Unbuilder[J]): T = lift(z.read(j, u)) - } - - @inline def ?[A](implicit z: A): A = z - - implicit val JNullJW: JW[JNull.type] = new JW[JNull.type] { def write[J](x: JNull.type, b: Builder[J]) = b.writeNull() } - implicit val JNullJR: JR[JNull.type] = new JR[JNull.type] { def read[J](j: Option[J], u: Unbuilder[J]) = JNull } - - implicit val JBooleanJW: JW[JBoolean] = unlift(_.get) - implicit val JBooleanJR: JR[JBoolean] = lift(JBoolean(_)) - - implicit val JStringJW: JW[JString] = unlift(_.value) - implicit val JStringJR: JR[JString] = lift(JString(_)) - - implicit val JNumberJW: JW[JNumber] = unlift(x => BigDecimal(x.value)) - implicit val JNumberJR: JR[JNumber] = lift((x: BigDecimal) => JNumber(x.toString)) - - implicit lazy val JArrayJW: JW[JArray] = unlift[JArray, Array[JValue]](_.value) + implicit val JBooleanJF: JF[JBoolean] = project(_.get, JBoolean(_)) + implicit val JStringJF: JF[JString] = project(_.value, JString(_)) + implicit val JNumberJF: JF[JNumber] = project(x => BigDecimal(x.value), (x: BigDecimal) => JNumber(x.toString)) + implicit val JArrayJF: JF[JArray] = project[JArray, Array[JValue]](_.value, JArray(_)) implicit lazy val JObjectJW: JW[JObject] = new JW[JObject] { def write[J](x: JObject, b: Builder[J]) = { @@ -44,12 +28,12 @@ trait JValueFormat { self: sjsonnew.BasicJsonProtocol => implicit lazy val JValueJW: JW[JValue] = new JW[JValue] { def write[J](x: JValue, b: Builder[J]) = x match { - case x: JNull.type => ?[JW[JNull.type]].write(x, b) - case x: JBoolean => ?[JW[JBoolean]].write(x, b) - case x: JString => ?[JW[JString]].write(x, b) - case x: JNumber => ?[JW[JNumber]].write(x, b) - case x: JObject => ?[JW[JObject]].write(x, b) - case x: JArray => ?[JW[JArray]].write(x, b) + case x: JNull.type => JNullJF.write(x, b) + case x: JBoolean => JBooleanJF.write(x, b) + case x: JString => JStringJF.write(x, b) + case x: JNumber => JNumberJF.write(x, b) + case x: JArray => JArrayJF.write(x, b) + case x: JObject => JObjectJW.write(x, b) } } From 70ac55d0b4c0d6def898802f72f8e50d7ec82f79 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Mar 2017 10:06:02 +0000 Subject: [PATCH 14/19] Encode success/failure in setting query response --- .../sbt/internal/server/SettingQuery.scala | 16 ++++---- .../sbt/protocol/SettingQueryFailure.scala | 32 ++++++++++++++++ .../sbt/protocol/SettingQueryResponse.scala | 37 ++++++++----------- .../sbt/protocol/SettingQuerySuccess.scala | 36 ++++++++++++++++++ .../protocol/codec/EventMessageFormats.scala | 4 +- .../sbt/protocol/codec/JsonProtocol.scala | 4 +- .../codec/SettingQueryFailureFormats.scala | 27 ++++++++++++++ .../codec/SettingQueryResponseFormats.scala | 21 +---------- .../codec/SettingQuerySuccessFormats.scala | 29 +++++++++++++++ protocol/src/main/contraband/server.contra | 9 ++++- 10 files changed, 163 insertions(+), 52 deletions(-) create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/SettingQueryFailure.scala create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/SettingQuerySuccess.scala create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFailureFormats.scala create mode 100644 protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQuerySuccessFormats.scala diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index 0a290b934..8407f76e3 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -98,19 +98,21 @@ object SettingQuery { case Left(_) => Converter toJsonUnsafe x.toString } - def getSettingJsonStringValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, JValue] = + def getSettingJsonValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, JValue] = getSettingValue(structure, key) map (toJson(_)(key.key.manifest)) def handleSettingQuery(req: SettingQuery, structure: BuildStructure): SettingQueryResponse = { val key = Parser.parse(req.setting, scopedKeyParser(structure)) - val result: Either[String, JValue] = key flatMap (getSettingJsonStringValue(structure, _)) + val result = + for { + key <- key + json <- getSettingJsonValue(structure, key) + } yield SettingQuerySuccess(json, key.key.manifest.toString) - val finalResult: JValue = result match { - case Right(j) => j - case Left(s) => Converter toJsonUnsafe s + result match { + case Right(x) => x + case Left(s) => SettingQueryFailure(s) } - - SettingQueryResponse(finalResult) } } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryFailure.scala b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryFailure.scala new file mode 100644 index 000000000..5369ddd9f --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryFailure.scala @@ -0,0 +1,32 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +final class SettingQueryFailure private ( + val message: String) extends sbt.protocol.SettingQueryResponse() with Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SettingQueryFailure => (this.message == x.message) + case _ => false + } + override def hashCode: Int = { + 37 * (17 + message.##) + } + override def toString: String = { + "SettingQueryFailure(" + message + ")" + } + protected[this] def copy(message: String = message): SettingQueryFailure = { + new SettingQueryFailure(message) + } + def withMessage(message: String): SettingQueryFailure = { + copy(message = message) + } +} +object SettingQueryFailure { + + def apply(message: String): SettingQueryFailure = new SettingQueryFailure(message) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala index 633801200..68ad82fee 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/SettingQueryResponse.scala @@ -4,29 +4,22 @@ // DO NOT EDIT MANUALLY package sbt.protocol -final class SettingQueryResponse private ( - val value: scala.json.ast.unsafe.JValue) extends sbt.protocol.EventMessage() with Serializable { - - - - override def equals(o: Any): Boolean = o match { - case x: SettingQueryResponse => (this.value == x.value) - case _ => false - } - override def hashCode: Int = { - 37 * (17 + value.##) - } - override def toString: String = { - "SettingQueryResponse(" + value + ")" - } - protected[this] def copy(value: scala.json.ast.unsafe.JValue = value): SettingQueryResponse = { - new SettingQueryResponse(value) - } - def withValue(value: scala.json.ast.unsafe.JValue): SettingQueryResponse = { - copy(value = value) - } +abstract class SettingQueryResponse() extends sbt.protocol.EventMessage() with Serializable { + + + + +override def equals(o: Any): Boolean = o match { + case x: SettingQueryResponse => true + case _ => false +} +override def hashCode: Int = { + 17 +} +override def toString: String = { + "SettingQueryResponse()" +} } object SettingQueryResponse { - def apply(value: scala.json.ast.unsafe.JValue): SettingQueryResponse = new SettingQueryResponse(value) } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/SettingQuerySuccess.scala b/protocol/src/main/contraband-scala/sbt/protocol/SettingQuerySuccess.scala new file mode 100644 index 000000000..a53aeb41f --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/SettingQuerySuccess.scala @@ -0,0 +1,36 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +final class SettingQuerySuccess private ( + val value: scala.json.ast.unsafe.JValue, + val contentType: String) extends sbt.protocol.SettingQueryResponse() with Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: SettingQuerySuccess => (this.value == x.value) && (this.contentType == x.contentType) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + value.##) + contentType.##) + } + override def toString: String = { + "SettingQuerySuccess(" + value + ", " + contentType + ")" + } + protected[this] def copy(value: scala.json.ast.unsafe.JValue = value, contentType: String = contentType): SettingQuerySuccess = { + new SettingQuerySuccess(value, contentType) + } + def withValue(value: scala.json.ast.unsafe.JValue): SettingQuerySuccess = { + copy(value = value) + } + def withContentType(contentType: String): SettingQuerySuccess = { + copy(contentType = contentType) + } +} +object SettingQuerySuccess { + + def apply(value: scala.json.ast.unsafe.JValue, contentType: String): SettingQuerySuccess = new SettingQuerySuccess(value, contentType) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala index d74e27dce..0727e822c 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala @@ -5,6 +5,6 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.JValueFormat with sbt.protocol.codec.SettingQueryResponseFormats => -implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat4[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQueryResponse]("type") +trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.JValueFormat with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats => +implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat5[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure]("type") } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala index e91f6606c..f755ceb8c 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala @@ -12,7 +12,9 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.internal.JValueFormat - with sbt.protocol.codec.SettingQueryResponseFormats + with sbt.protocol.codec.SettingQuerySuccessFormats + with sbt.protocol.codec.SettingQueryFailureFormats with sbt.protocol.codec.EventMessageFormats + with sbt.protocol.codec.SettingQueryResponseFormats with sbt.protocol.codec.ExecutionEventFormats object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFailureFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFailureFormats.scala new file mode 100644 index 000000000..1ad39d383 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryFailureFormats.scala @@ -0,0 +1,27 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait SettingQueryFailureFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val SettingQueryFailureFormat: JsonFormat[sbt.protocol.SettingQueryFailure] = new JsonFormat[sbt.protocol.SettingQueryFailure] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQueryFailure = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val message = unbuilder.readField[String]("message") + unbuilder.endObject() + sbt.protocol.SettingQueryFailure(message) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.SettingQueryFailure, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("message", obj.message) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala index 45d0756c8..9e0aebed9 100644 --- a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQueryResponseFormats.scala @@ -5,23 +5,6 @@ // DO NOT EDIT MANUALLY package sbt.protocol.codec import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } -trait SettingQueryResponseFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol => -implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = new JsonFormat[sbt.protocol.SettingQueryResponse] { - override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQueryResponse = { - jsOpt match { - case Some(js) => - unbuilder.beginObject(js) - val value = unbuilder.readField[scala.json.ast.unsafe.JValue]("value") - unbuilder.endObject() - sbt.protocol.SettingQueryResponse(value) - case None => - deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.protocol.SettingQueryResponse, builder: Builder[J]): Unit = { - builder.beginObject() - builder.addField("value", obj.value) - builder.endObject() - } -} +trait SettingQueryResponseFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol with sbt.protocol.codec.SettingQuerySuccessFormats with sbt.protocol.codec.SettingQueryFailureFormats => +implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = flatUnionFormat2[sbt.protocol.SettingQueryResponse, sbt.protocol.SettingQuerySuccess, sbt.protocol.SettingQueryFailure]("type") } diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQuerySuccessFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQuerySuccessFormats.scala new file mode 100644 index 000000000..431a23d71 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/SettingQuerySuccessFormats.scala @@ -0,0 +1,29 @@ +/** + * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait SettingQuerySuccessFormats { self: sbt.internal.JValueFormat with sjsonnew.BasicJsonProtocol => +implicit lazy val SettingQuerySuccessFormat: JsonFormat[sbt.protocol.SettingQuerySuccess] = new JsonFormat[sbt.protocol.SettingQuerySuccess] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQuerySuccess = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val value = unbuilder.readField[scala.json.ast.unsafe.JValue]("value") + val contentType = unbuilder.readField[String]("contentType") + unbuilder.endObject() + sbt.protocol.SettingQuerySuccess(value, contentType) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.SettingQuerySuccess, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("value", obj.value) + builder.addField("contentType", obj.contentType) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband/server.contra b/protocol/src/main/contraband/server.contra index 7d9798aa8..bf92437c4 100644 --- a/protocol/src/main/contraband/server.contra +++ b/protocol/src/main/contraband/server.contra @@ -40,8 +40,15 @@ type ExecStatusEvent implements EventMessage { commandQueue: [String] } -type SettingQueryResponse implements EventMessage { +interface SettingQueryResponse implements EventMessage {} + +type SettingQuerySuccess implements SettingQueryResponse { value: scala.json.ast.unsafe.JValue! + contentType: String! +} + +type SettingQueryFailure implements SettingQueryResponse { + message: String! } # enum Status { From 63fcca55435279b79d89079a8567d95bdd55d2d7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Mar 2017 11:48:40 +0100 Subject: [PATCH 15/19] Test setting query functionality --- main/src/main/scala/sbt/internal/Load.scala | 2 +- .../internal/server/SettingQueryTest.scala | 180 ++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 main/src/test/scala/sbt/internal/server/SettingQueryTest.scala diff --git a/main/src/main/scala/sbt/internal/Load.scala b/main/src/main/scala/sbt/internal/Load.scala index 76e0f907c..8a3eae709 100755 --- a/main/src/main/scala/sbt/internal/Load.scala +++ b/main/src/main/scala/sbt/internal/Load.scala @@ -699,7 +699,7 @@ private[sbt] object Load { * sbt file to resolve a project. * @param log A logger to report auto-plugin issues to. */ - private[this] def resolveProject( + private[sbt] def resolveProject( p: Project, projectPlugins: Seq[AutoPlugin], loadedPlugins: LoadedPlugins, diff --git a/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala new file mode 100644 index 000000000..d3f73505f --- /dev/null +++ b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala @@ -0,0 +1,180 @@ +package sbt +package internal +package server + +import java.io._ +import java.net._ +import java.nio.file._ +import java.util.concurrent._ + +import scala.collection.mutable + +import xsbti._ +import sbt.io.IO +import sbt.internal.util._ +import sbt.internal.BuildStreams.{ Streams => _, _ } +import sbt.internal.Load._ +import sbt.util._ +import sbt.BuildPaths._ +import sbt.Def.{ ScopeLocal, ScopedKey, Setting } +import sbt.Keys._ + +object SettingQueryTest extends org.specs2.mutable.Specification { + implicit class PathOps(val path: Path) extends AnyVal { + def /(other: String): Path = if (other == ".") path else path resolve other + } + + val baseDir: Path = Files createTempDirectory "sbt-setting-query-test" + val globalDir: Path = Files createTempDirectory "sbt-setting-query-test-global-dir" + val bootDir: Path = Files createTempDirectory "sbt-setting-query-test-boot-dir" + val ivyHome: Path = Files createTempDirectory "sbt-setting-query-test-ivy-home" + val logFile: File = File.createTempFile("sbt", ".log") + + val baseFile: File = baseDir.toFile + val baseUri: URI = IO directoryURI baseFile + IO assertAbsolute baseUri + + val globalDirFile: File = globalDir.toFile + + def ??? : Nothing = { Thread.dumpStack(); throw new NotImplementedError } + + val noopLoader: ClassLoader = new URLClassLoader(Array(), null) + + object NoGlobalLock extends GlobalLock { def apply[T](lockFile: File, run: Callable[T]) = run.call() } + + lazy val structure: BuildStructure = { + val projectSettings: Seq[Setting[_]] = Seq(scalaVersion := "2.12.1") + + val appConfig: AppConfiguration = new AppConfiguration { + def baseDirectory(): File = baseFile + def arguments(): Array[String] = Array() + def provider(): AppProvider = new AppProvider { + def scalaProvider(): ScalaProvider = new ScalaProvider { scalaProvider => + def launcher(): Launcher = new Launcher { + def getScala(version: String): ScalaProvider = getScala(version, "") + def getScala(version: String, reason: String): ScalaProvider = getScala(version, reason, "org.scala-lang") + def getScala(version: String, reason: String, scalaOrg: String): ScalaProvider = scalaProvider + + def app(id: ApplicationID, version: String): AppProvider = ??? + + def topLoader(): ClassLoader = noopLoader + def globalLock(): GlobalLock = NoGlobalLock + def bootDirectory(): File = bootDir.toFile + def ivyRepositories(): Array[Repository] = Array() + def appRepositories(): Array[Repository] = Array() + def isOverrideRepositories: Boolean = false + def ivyHome(): File = SettingQueryTest.this.ivyHome.toFile + def checksums(): Array[String] = Array() + } + def version(): String = "2.12.1" + + def loader(): ClassLoader = noopLoader + def jars(): Array[File] = Array(libraryJar, compilerJar) + + def libraryJar(): File = new File("scala-library.jar") + def compilerJar(): File = new File("scala-compiler.jar") + + def app(id: ApplicationID): AppProvider = ??? + } + + def id(): ApplicationID = sbt.ApplicationID( + "org.scala-sbt", "sbt", "0.13.13", "sbt.xMain", + components = Seq(), crossVersionedValue = CrossValue.Disabled, extra = Seq() + ) + + def loader(): ClassLoader = noopLoader + + def entryPoint(): Class[_] = ??? + def mainClass(): Class[_ <: AppMain] = ??? + def newMain(): AppMain = ??? + + def mainClasspath(): Array[File] = Array() + + def components(): ComponentProvider = new ComponentProvider { + def componentLocation(id: String): File = ??? + def component(componentID: String): Array[File] = ??? + def defineComponent(componentID: String, components: Array[File]): Unit = ??? + def addToComponent(componentID: String, components: Array[File]): Boolean = ??? + def lockFile(): File = ??? + } + } + } + + val state: State = + StandardMain.initialState(appConfig, initialDefinitions = Seq(), preCommands = Seq()) + .put(globalBaseDirectory, globalDirFile) + + val config0 = defaultPreGlobal(state, baseFile, globalDirFile, state.log) + val config = defaultWithGlobal(state, baseFile, config0, globalDirFile, state.log) + + val buildUnit: BuildUnit = { + val loadedPlugins: LoadedPlugins = + noPlugins(projectStandard(baseFile), config.copy(pluginManagement = config.pluginManagement.forPlugin)) + + val project: Project = { + val project0 = Project("t", baseFile) settings projectSettings + val fileToLoadedSbtFileMap = new mutable.HashMap[File, LoadedSbtFile] + val autoPlugins = loadedPlugins.detected.deducePluginsFromProject(project0, state.log) + val injectSettings = config.injectSettings + resolveProject(project0, autoPlugins, loadedPlugins, injectSettings, fileToLoadedSbtFileMap, state.log) + } + + val projects: Seq[Project] = Seq(project) + val builds: Seq[BuildDef] = BuildDef.defaultAggregated(project.id, Nil) :: Nil + val defs: LoadedDefinitions = new LoadedDefinitions(baseFile, Nil, noopLoader, builds, projects, Nil) + new BuildUnit(baseUri, baseFile, defs, loadedPlugins) + } + + val (partBuildUnit: PartBuildUnit, projectRefs: List[ProjectReference]) = loaded(buildUnit) + val partBuildUnits: Map[URI, PartBuildUnit] = Map(buildUnit.uri -> partBuildUnit) + val allProjectRefs: Map[URI, List[ProjectReference]] = Map(buildUnit.uri -> projectRefs) + checkAll(allProjectRefs, partBuildUnits) + + val partBuild: PartBuild = new PartBuild(baseUri, partBuildUnits) + val loadedBuild: LoadedBuild = resolveProjects(partBuild) + + val units: Map[URI, LoadedBuildUnit] = loadedBuild.units + + val settings: Seq[Setting[_]] = finalTransforms(buildConfigurations(loadedBuild, getRootProject(units), config.injectSettings)) + val delegates: Scope => Seq[Scope] = defaultDelegates(loadedBuild) + val scopeLocal: ScopeLocal = EvaluateTask.injectStreams + val display: Show[ScopedKey[_]] = Project showLoadingKey loadedBuild + + val data: Settings[Scope] = Def.make(settings)(delegates, scopeLocal, display) + val extra: KeyIndex => BuildUtil[_] = index => BuildUtil(baseUri, units, index, data) + + val index: StructureIndex = structureIndex(data, settings, extra, units) + val streams: State => Streams = mkStreams(units, baseUri, data) + + val structure: BuildStructure = + new BuildStructure(units, baseUri, settings, data, index, streams, delegates, scopeLocal) + + structure + } + + def query(setting: String): String = { + import sbt.protocol._ + val req: SettingQuery = protocol.SettingQuery(setting) + val rsp: SettingQueryResponse = server.SettingQuery.handleSettingQuery(req, structure) + val bytes: Array[Byte] = Serialization serializeEventMessage rsp + val payload: String = new String(bytes, java.nio.charset.StandardCharsets.UTF_8) + payload + } + + // -.- avoid specs2's ko/ok + import org.specs2.matcher.MatchResult + def qok(x: String, t: String): String => MatchResult[Any] = query(_) must_== """{"type":"SettingQuerySuccess","value":""" + x + ""","contentType":"""" + t + """"}""" + def qko(msg: String): String => MatchResult[Any] = query(_) must_== """{"type":"SettingQueryFailure","message":"""" + msg + """"}""" + + "setting query" should { + "t/scalaVersion" in qok("\"2.12.1\"", "java.lang.String") + "t/pollInterval" in qok("500", "Int") + "t/sourcesInBase" in qok("true", "Boolean") + "t/startYear" in qok("null", "scala.Option[Int]") + "t/scalaArtifacts" in qok("""["scala-library","scala-compiler","scala-reflect","scala-actors","scalap"]""", "scala.collection.Seq[java.lang.String]") + + "scalaVersion" in qko("Not a valid project ID: scalaVersion\\nscalaVersion\\n ^") + "t/scalacOptions" in qko(s"Key {$baseUri}t/compile:scalacOptions is a task, can only query settings") + "t/fooo" in qko("Expected ':' (if selecting a configuration)\\nNot a valid key: fooo (similar: fork)\\nt/fooo\\n ^") + } +} From 6950a1e06188dd161f2ee18215b172aeec8a50a2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Mar 2017 11:00:24 +0000 Subject: [PATCH 16/19] Use the OptJsonWriter on AttributeKey --- .../internal/server/JsonFormatRegistry.scala | 120 ------------------ .../sbt/internal/server/SettingQuery.scala | 20 +-- 2 files changed, 10 insertions(+), 130 deletions(-) delete mode 100644 main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala diff --git a/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala b/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala deleted file mode 100644 index 5a6daa5fa..000000000 --- a/main/src/main/scala/sbt/internal/server/JsonFormatRegistry.scala +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2017 Lightbend Inc. - */ -package sbt -package internal -package server - -import java.io.File -import java.net.{ URI, URL } -import scala.{ collection => sc }, sc.{ immutable => sci }, sci.{ Seq => sciSeq } -import sbt.librarymanagement.LibraryManagementCodec._ -import sjsonnew.{ JsonFormat => JF } - -/** A registry of JsonFormat instances. - * - * Used to lookup, given a value of type 'A', its 'JsonFormat[A]' instance. - */ -object JsonFormatRegistry { - @inline def ?[A](implicit z: A): A = z - - val UnitJF: JF[Unit] = ? - val BooleanJF: JF[Boolean] = ? - val ByteJF: JF[Byte] = ? - val ShortJF: JF[Short] = ? - val CharJF: JF[Char] = ? - val IntJF: JF[Int] = ? - val LongJF: JF[Int] = ? - val FloatJF: JF[Float] = ? - val DoubleJF: JF[Double] = ? - val StringJF: JF[String] = ? - val SymbolJF: JF[Symbol] = ? - val FileJF: JF[File] = ? - val URIJF: JF[URI] = ? - val URLJF: JF[URL] = ? - - def cast[A](z: JF[_]): JF[A] = z.asInstanceOf[JF[A]] - def castAndWrap[A](z: JF[_]): Option[JF[A]] = Some(cast(z)) - - // TODO: Any way to de-duplify here? - def tuple1JF[A: Manifest]: Option[JF[ Tuple1[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } - def optionJF[A: Manifest]: Option[JF[ Option[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } - def listJF[A: Manifest]: Option[JF[ List[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } - def arrayJF[A: Manifest]: Option[JF[ Array[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } - def vectorJF[A: Manifest]: Option[JF[ Vector[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } - def sciSeqJF[A: Manifest]: Option[JF[ sciSeq[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } - def seqJF[A: Manifest]: Option[JF[ Seq[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } -//def optionalJF[A: Manifest]: Option[JF[Optional[A]]] = lookup(manifest[A]) map { implicit elemJF: JF[A] => ? } // TODO: Upgrade sjsonnew version - - def eitherJF[A: Manifest, B: Manifest]: Option[JF[Either[A, B]]] = - for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B])) yield { - implicit val aJFi: JF[A] = aJF - implicit val bJFi: JF[B] = bJF - ? - } - - - def tuple2JF[A: Manifest, B: Manifest]: Option[JF[(A, B)]] = - for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B])) yield { - implicit val aJFi: JF[A] = aJF - implicit val bJFi: JF[B] = bJF - ? - } - - def tuple3JF[A: Manifest, B: Manifest, C: Manifest]: Option[JF[(A, B, C)]] = - for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B]); cJF <- lookup(manifest[C])) yield { - implicit val aJFi: JF[A] = aJF - implicit val bJFi: JF[B] = bJF - implicit val cJFi: JF[C] = cJF - ? - } - - def tuple4JF[A: Manifest, B: Manifest, C: Manifest, D: Manifest]: Option[JF[(A, B, C, D)]] = - for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[B]); cJF <- lookup(manifest[C]); dJF <- lookup(manifest[D])) yield { - implicit val aJFi: JF[A] = aJF - implicit val bJFi: JF[B] = bJF - implicit val cJFi: JF[C] = cJF - implicit val dJFi: JF[D] = dJF - ? - } - - // Map is a PITA, because it needs a JsonKeyFormat, note the "Key" -// def mapJF[A: Manifest, B: Manifest]: Option[JF[Map[A, B]]] = -// for (aJF <- lookup(manifest[A]); bJF <- lookup(manifest[A])) yield { -// implicit val aJFi: JF[A] = aJF -// implicit val bJFi: JF[B] = bJF -// ? -// } -// } - - def lookup[A: Manifest]: Option[JF[A]] = manifest[A] match { - case Manifest.Unit => castAndWrap[A]( UnitJF) - case Manifest.Boolean => castAndWrap[A](BooleanJF) - case Manifest.Byte => castAndWrap[A]( ByteJF) - case Manifest.Short => castAndWrap[A]( ShortJF) - case Manifest.Char => castAndWrap[A]( CharJF) - case Manifest.Int => castAndWrap[A]( IntJF) - case Manifest.Long => castAndWrap[A]( LongJF) - case Manifest.Float => castAndWrap[A]( FloatJF) - case Manifest.Double => castAndWrap[A]( DoubleJF) - case m if m.runtimeClass == classOf[String] => castAndWrap[A]( StringJF) - case m if m.runtimeClass == classOf[Symbol] => castAndWrap[A]( SymbolJF) - case m if m.runtimeClass == classOf[File] => castAndWrap[A]( FileJF) - case m if m.runtimeClass == classOf[URI] => castAndWrap[A]( URIJF) - case m if m.runtimeClass == classOf[URL] => castAndWrap[A]( URLJF) - case m if m.runtimeClass == classOf[Option[_]] => optionJF(m.typeArguments.head) map cast - case m if m.runtimeClass == classOf[Either[_, _]] => eitherJF(m.typeArguments.head, m.typeArguments(1)) map cast - case m if m.runtimeClass == classOf[Tuple1[_]] => tuple1JF(m.typeArguments.head) map cast - case m if m.runtimeClass == classOf[(_, _)] => tuple2JF(m.typeArguments.head, m.typeArguments(1)) map cast - case m if m.runtimeClass == classOf[(_, _, _)] => tuple3JF(m.typeArguments.head, m.typeArguments(1), m.typeArguments(2)) map cast - case m if m.runtimeClass == classOf[(_, _, _, _)] => tuple4JF(m.typeArguments.head, m.typeArguments(1), m.typeArguments(2), m.typeArguments(3)) map cast - case m if m.runtimeClass == classOf[List[_]] => listJF(m.typeArguments.head) map cast - case m if m.runtimeClass == classOf[Array[_]] => arrayJF(m.typeArguments.head) map cast -// case m if m.runtimeClass == classOf[Map[_, _]] => mapJF(m.typeArguments.head, m.typeArguments(1)) map cast - case m if m.runtimeClass == classOf[Vector[_]] => vectorJF(m.typeArguments.head) map cast - case m if m.runtimeClass == classOf[sciSeq[_]] => sciSeqJF(m.typeArguments.head) map cast - case m if m.runtimeClass == classOf[Seq[_]] => seqJF(m.typeArguments.head) map cast -// case m if m.runtimeClass == classOf[Optional[_]] => optionalJF(m.typeArguments.head) map cast - case _ => None - } -} diff --git a/main/src/main/scala/sbt/internal/server/SettingQuery.scala b/main/src/main/scala/sbt/internal/server/SettingQuery.scala index 8407f76e3..54fbc7b42 100644 --- a/main/src/main/scala/sbt/internal/server/SettingQuery.scala +++ b/main/src/main/scala/sbt/internal/server/SettingQuery.scala @@ -8,8 +8,10 @@ package server import java.net.URI import scala.json.ast.unsafe.JValue import scala.util.{ Left, Right } +import sbt.util.{ SomeJsonWriter, NoJsonWriter } import sbt.librarymanagement.LibraryManagementCodec._ import sbt.protocol._ +import sjsonnew._ import sjsonnew.support.scalajson.unsafe._ object SettingQuery { @@ -87,19 +89,17 @@ object SettingQuery { case x => Right(x) } - def toJsonStringStrict[A: Manifest](x: A): Either[String, JValue] = - JsonFormatRegistry.lookup[A] - .toRight(s"JsonWriter for ${manifest[A]} not found") - .map(implicit jsonWriter => Converter toJsonUnsafe x) + def getJsonWriter[A](key: AttributeKey[A]): Either[String, JsonWriter[A]] = key.optJsonWriter match { + case SomeJsonWriter(jw) => Right(jw) + case NoJsonWriter() => Left(s"JsonWriter for ${key.manifest} not found") + } - def toJson[A: Manifest](x: A): JValue = - toJsonStringStrict(x) match { - case Right(j) => j - case Left(_) => Converter toJsonUnsafe x.toString - } + def toJson[A: JsonWriter](x: A): JValue = Converter toJsonUnsafe x def getSettingJsonValue[A](structure: BuildStructure, key: Def.ScopedKey[A]): Either[String, JValue] = - getSettingValue(structure, key) map (toJson(_)(key.key.manifest)) + getSettingValue(structure, key) flatMap (value => + getJsonWriter(key.key) map { implicit jw: JsonWriter[A] => toJson(value) } + ) def handleSettingQuery(req: SettingQuery, structure: BuildStructure): SettingQueryResponse = { val key = Parser.parse(req.setting, scopedKeyParser(structure)) From ad2f91e357bb9a8e4c95d07c0086db51a2023487 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Mar 2017 13:14:07 +0100 Subject: [PATCH 17/19] Extra getName/getImplicit in KeyMacro --- .../src/main/scala/sbt/std/KeyMacro.scala | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/main-settings/src/main/scala/sbt/std/KeyMacro.scala b/main-settings/src/main/scala/sbt/std/KeyMacro.scala index e770d0b43..243eeb6fb 100644 --- a/main-settings/src/main/scala/sbt/std/KeyMacro.scala +++ b/main-settings/src/main/scala/sbt/std/KeyMacro.scala @@ -1,7 +1,8 @@ package sbt package std -import reflect.macros._ +import scala.annotation.tailrec +import scala.reflect.macros._ private[sbt] object KeyMacro { def settingKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[SettingKey[T]] = @@ -17,20 +18,28 @@ private[sbt] object KeyMacro { c.universe.reify { InputKey[T](name.splice, description.splice)(mf.splice) } } - def keyImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)(f: (c.Expr[String], c.Expr[Manifest[T]]) => c.Expr[S]): c.Expr[S] = - { - import c.universe._ - val enclosingValName = definingValName(c, methodName => s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""") - val name = c.Expr[String](Literal(Constant(enclosingValName))) - val mf = c.Expr[Manifest[T]](c.inferImplicitValue(weakTypeOf[Manifest[T]])) - f(name, mf) - } + def keyImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)( + f: (c.Expr[String], c.Expr[Manifest[T]]) => c.Expr[S] + ): c.Expr[S] = + f(getName(c), getImplicit[Manifest[T]](c)) + + private def getName[S: c.WeakTypeTag, T: c.WeakTypeTag](c: blackbox.Context): c.Expr[String] = { + import c.universe._ + val enclosingValName = definingValName(c, methodName => s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""") + c.Expr[String](Literal(Constant(enclosingValName))) + } + + private def getImplicit[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[T] = { + import c.universe._ + c.Expr[T](c.inferImplicitValue(weakTypeOf[T])) + } + def definingValName(c: blackbox.Context, invalidEnclosingTree: String => String): String = { import c.universe.{ Apply => ApplyTree, _ } val methodName = c.macroApplication.symbol.name def processName(n: Name): String = n.decodedName.toString.trim // trim is not strictly correct, but macros don't expose the API necessary - def enclosingVal(trees: List[c.Tree]): String = + @tailrec def enclosingVal(trees: List[c.Tree]): String = { trees match { case vd @ ValDef(_, name, _, _) :: ts => processName(name) @@ -44,6 +53,7 @@ private[sbt] object KeyMacro { } enclosingVal(enclosingTrees(c).toList) } + def enclosingTrees(c: blackbox.Context): Seq[c.Tree] = c.asInstanceOf[reflect.macros.runtime.Context].callsiteTyper.context.enclosingContextChain.map(_.tree.asInstanceOf[c.Tree]) } From 52de082b2e73865c085d52ec84e9ea7787ca70e7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Mar 2017 13:16:55 +0100 Subject: [PATCH 18/19] Add OptJsonWriter to SettingKey --- .../src/main/scala/sbt/Structure.scala | 19 ++++++++++--------- .../src/main/scala/sbt/std/KeyMacro.scala | 11 +++++++++-- main/src/main/scala/sbt/Keys.scala | 1 + 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index 5752c7e96..c06c34bf0 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -3,19 +3,20 @@ */ package sbt -/** An abstraction on top of Settings for build configuration and task definition. */ +import scala.language.experimental.macros import java.io.File -import ConcurrentRestrictions.Tag -import Def.{ Initialize, KeyedInitialize, ScopedKey, Setting, setting } import sbt.io.{ FileFilter, PathFinder } import sbt.io.syntax._ -import std.TaskExtra.{ task => mktask, _ } import sbt.internal.util.Types._ import sbt.internal.util.{ ~>, AList, AttributeKey, Settings, SourcePosition } +import sbt.util.OptJsonWriter +import sbt.ConcurrentRestrictions.Tag +import sbt.Def.{ Initialize, KeyedInitialize, ScopedKey, Setting, setting } +import std.TaskExtra.{ task => mktask, _ } -import language.experimental.macros +/** An abstraction on top of Settings for build configuration and task definition. */ sealed trait Scoped { def scope: Scope; val key: AttributeKey[_] } @@ -471,17 +472,17 @@ object TaskKey { /** Constructs SettingKeys, which are associated with a value to define a basic setting.*/ object SettingKey { - def apply[T: Manifest](label: String, description: String = "", rank: Int = KeyRanks.DefaultSettingRank): SettingKey[T] = + def apply[T: Manifest: OptJsonWriter](label: String, description: String = "", rank: Int = KeyRanks.DefaultSettingRank): SettingKey[T] = apply(AttributeKey[T](label, description, rank)) - def apply[T: Manifest](label: String, description: String, extend1: Scoped, extendN: Scoped*): SettingKey[T] = + def apply[T: Manifest: OptJsonWriter](label: String, description: String, extend1: Scoped, extendN: Scoped*): SettingKey[T] = apply(AttributeKey[T](label, description, extendScoped(extend1, extendN))) - def apply[T: Manifest](label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped*): SettingKey[T] = + def apply[T: Manifest: OptJsonWriter](label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped*): SettingKey[T] = apply(AttributeKey[T](label, description, extendScoped(extend1, extendN), rank)) def apply[T](akey: AttributeKey[T]): SettingKey[T] = new SettingKey[T] { val key = akey; def scope = Scope.ThisScope } - def local[T: Manifest]: SettingKey[T] = apply[T](AttributeKey.local[T]) + def local[T: Manifest: OptJsonWriter]: SettingKey[T] = apply[T](AttributeKey.local[T]) } diff --git a/main-settings/src/main/scala/sbt/std/KeyMacro.scala b/main-settings/src/main/scala/sbt/std/KeyMacro.scala index 243eeb6fb..bf0af5ee1 100644 --- a/main-settings/src/main/scala/sbt/std/KeyMacro.scala +++ b/main-settings/src/main/scala/sbt/std/KeyMacro.scala @@ -4,10 +4,12 @@ package std import scala.annotation.tailrec import scala.reflect.macros._ +import sbt.util.OptJsonWriter + private[sbt] object KeyMacro { def settingKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[SettingKey[T]] = - keyImpl[T, SettingKey[T]](c) { (name, mf) => - c.universe.reify { SettingKey[T](name.splice, description.splice)(mf.splice) } + keyImpl2[T, SettingKey[T]](c) { (name, mf, ojw) => + c.universe.reify { SettingKey[T](name.splice, description.splice)(mf.splice, ojw.splice) } } def taskKeyImpl[T: c.WeakTypeTag](c: blackbox.Context)(description: c.Expr[String]): c.Expr[TaskKey[T]] = keyImpl[T, TaskKey[T]](c) { (name, mf) => @@ -23,6 +25,11 @@ private[sbt] object KeyMacro { ): c.Expr[S] = f(getName(c), getImplicit[Manifest[T]](c)) + private def keyImpl2[T: c.WeakTypeTag, S: c.WeakTypeTag](c: blackbox.Context)( + f: (c.Expr[String], c.Expr[Manifest[T]], c.Expr[OptJsonWriter[T]]) => c.Expr[S] + ): c.Expr[S] = + f(getName(c), getImplicit[Manifest[T]](c), getImplicit[OptJsonWriter[T]](c)) + private def getName[S: c.WeakTypeTag, T: c.WeakTypeTag](c: blackbox.Context): c.Expr[String] = { import c.universe._ val enclosingValName = definingValName(c, methodName => s"""$methodName must be directly assigned to a val, such as `val x = $methodName[Int]("description")`.""") diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index bb42f5105..0559fc834 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -34,6 +34,7 @@ import sbt.internal.io.WatchState import sbt.internal.util.{ AttributeKey, CacheStore, SourcePosition } import sbt.librarymanagement.Configurations.CompilerPlugin +import sbt.librarymanagement.LibraryManagementCodec._ import sbt.librarymanagement.{ Artifact, Configuration, From 30bf72482b7835afb27f3d4e6041e4e16abfb7bc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 27 Mar 2017 13:06:42 +0100 Subject: [PATCH 19/19] Add test for a LM type Use libraryDependencies which is a Seq[ModuleID] --- .../src/test/scala/sbt/internal/server/SettingQueryTest.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala index d3f73505f..7be5082fc 100644 --- a/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala +++ b/main/src/test/scala/sbt/internal/server/SettingQueryTest.scala @@ -173,6 +173,10 @@ object SettingQueryTest extends org.specs2.mutable.Specification { "t/startYear" in qok("null", "scala.Option[Int]") "t/scalaArtifacts" in qok("""["scala-library","scala-compiler","scala-reflect","scala-actors","scalap"]""", "scala.collection.Seq[java.lang.String]") + "t/libraryDependencies" in qok( + """[{"organization":"org.scala-lang","name":"scala-library","revision":"2.12.1","isChanging":false,"isTransitive":true,"isForce":false,"crossVersion":{"type":"Disabled"}}]""", + "scala.collection.Seq[sbt.librarymanagement.ModuleID]") + "scalaVersion" in qko("Not a valid project ID: scalaVersion\\nscalaVersion\\n ^") "t/scalacOptions" in qko(s"Key {$baseUri}t/compile:scalacOptions is a task, can only query settings") "t/fooo" in qko("Expected ':' (if selecting a configuration)\\nNot a valid key: fooo (similar: fork)\\nt/fooo\\n ^")