diff --git a/build.sbt b/build.sbt index d753b42a8..b60629fef 100644 --- a/build.sbt +++ b/build.sbt @@ -175,9 +175,20 @@ lazy val actionsProj = (project in file("main-actions")). addSbtZinc, addSbtCompilerIvyIntegration, addSbtCompilerInterface, addSbtIO, addSbtUtilLogging, addSbtUtilRelation, addSbtLm, addSbtUtilTracking) +lazy val protocolProj = (project in file("protocol")). + enablePlugins(ContrabandPlugin, JsonCodecPlugin). + settings( + testedBaseSettings, + name := "Protocol", + libraryDependencies ++= Seq(sjsonNewScalaJson), + sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala" + ). + configure(addSbtUtilLogging) + // General command support and core commands not specific to a build system lazy val commandProj = (project in file("main-command")). enablePlugins(ContrabandPlugin, JsonCodecPlugin). + dependsOn(protocolProj). settings( testedBaseSettings, name := "Command", @@ -248,7 +259,7 @@ lazy val myProvided = config("provided") intransitive def allProjects = Seq( testingProj, testAgentProj, taskProj, stdTaskProj, runProj, - scriptedSbtProj, scriptedPluginProj, + scriptedSbtProj, scriptedPluginProj, protocolProj, actionsProj, commandProj, mainSettingsProj, mainProj, sbtProj, bundledLauncherProj) def projectsWithMyProvided = allProjects.map(p => p.copy(configurations = (p.configurations.filter(_ != Provided)) :+ myProvided)) diff --git a/main-command/src/main/scala/sbt/internal/NetworkChannel.scala b/main-command/src/main/scala/sbt/internal/NetworkChannel.scala index 5e21524ff..e2e475086 100644 --- a/main-command/src/main/scala/sbt/internal/NetworkChannel.scala +++ b/main-command/src/main/scala/sbt/internal/NetworkChannel.scala @@ -2,6 +2,7 @@ package sbt package internal import sbt.internal.server._ +import sbt.protocol._ import BasicKeys._ private[sbt] final class NetworkChannel extends CommandChannel { @@ -13,11 +14,10 @@ private[sbt] final class NetworkChannel extends CommandChannel { case Some(x) => x case None => 5001 } - def onCommand(command: internal.server.Command): Unit = { + def onCommand(command: CommandMessage): Unit = command match { - case Execution(cmd) => append(Exec(CommandSource.Network, cmd)) + case x: ExecCommand => append(Exec(CommandSource.Network, x.commandLine)) } - } server match { case Some(x) => // do nothing case _ => @@ -36,8 +36,8 @@ private[sbt] final class NetworkChannel extends CommandChannel { def publishStatus(cmdStatus: CommandStatus, lastSource: Option[CommandSource]): Unit = { server.foreach(server => server.publish( - if (cmdStatus.canEnter) StatusEvent(Ready) - else StatusEvent(Processing("TODO current command", cmdStatus.state.remainingCommands)) + if (cmdStatus.canEnter) StatusEvent("Ready", Vector()) + else StatusEvent("Processing", cmdStatus.state.remainingCommands.toVector) )) } } diff --git a/main-command/src/main/scala/sbt/internal/server/ClientConnection.scala b/main-command/src/main/scala/sbt/internal/server/ClientConnection.scala index 26b8188fb..37380b72c 100644 --- a/main-command/src/main/scala/sbt/internal/server/ClientConnection.scala +++ b/main-command/src/main/scala/sbt/internal/server/ClientConnection.scala @@ -7,6 +7,7 @@ package server import java.net.{ SocketTimeoutException, Socket } import java.util.concurrent.atomic.AtomicBoolean +import sbt.protocol._ abstract class ClientConnection(connection: Socket) { @@ -59,7 +60,7 @@ abstract class ClientConnection(connection: Socket) { out.flush() } - def onCommand(command: Command): Unit + def onCommand(command: CommandMessage): Unit def shutdown(): Unit = { println("Shutting down client connection") diff --git a/main-command/src/main/scala/sbt/internal/server/Serialization.scala b/main-command/src/main/scala/sbt/internal/server/Serialization.scala index a3d4736c1..cc1c7e14d 100644 --- a/main-command/src/main/scala/sbt/internal/server/Serialization.scala +++ b/main-command/src/main/scala/sbt/internal/server/Serialization.scala @@ -5,76 +5,37 @@ package sbt package internal package server -import sjsonnew.{ JsonFormat, BasicJsonProtocol } import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter } import scala.json.ast.unsafe.JValue import sjsonnew.support.scalajson.unsafe.Parser import java.nio.ByteBuffer import scala.util.{ Success, Failure } +import sbt.protocol._ object Serialization { - def serialize(event: Event): Array[Byte] = + def serialize(event: EventMessage): Array[Byte] = { - import ServerCodec._ - val msg = toMessage(event) - val json: JValue = Converter.toJson[EventMessage](msg).get + import codec.JsonProtocol._ + val json: JValue = Converter.toJson[EventMessage](event).get CompactPrinter(json).getBytes("UTF-8") } - def toMessage(event: Event): EventMessage = - event match { - case LogEvent(level, message) => - EventMessage( - `type` = "logEvent", - status = None, commandQueue = Vector(), - level = Some(level), message = Some(message), success = None, commandLine = None - ) - case StatusEvent(Ready) => - EventMessage( - `type` = "statusEvent", - status = Some("ready"), commandQueue = Vector(), - level = None, message = None, success = None, commandLine = None - ) - case StatusEvent(Processing(command, commandQueue)) => - EventMessage( - `type` = "statusEvent", - status = Some("processing"), commandQueue = commandQueue.toVector, - level = None, message = None, success = None, commandLine = None - ) - case ExecutionEvent(command, status) => - EventMessage( - `type` = "executionEvent", - status = None, commandQueue = Vector(), - level = None, message = None, success = Some(status), commandLine = Some(command) - ) - } /** * @return A command or an invalid input description */ - def deserialize(bytes: Seq[Byte]): Either[String, Command] = + def deserialize(bytes: Seq[Byte]): Either[String, CommandMessage] = { val buffer = ByteBuffer.wrap(bytes.toArray) Parser.parseFromByteBuffer(buffer) match { case Success(json) => - import ServerCodec._ + import codec.JsonProtocol._ Converter.fromJson[CommandMessage](json) match { - case Success(command) => - command.`type` match { - case "exec" => - command.commandLine match { - case Some(cmd) => Right(Execution(cmd)) - case None => Left("Missing or invalid command_line field") - } - case cmd => Left(s"Unknown command type $cmd") - } - case Failure(e) => Left(e.getMessage) + case Success(command) => Right(command) + case Failure(e) => Left(e.getMessage) } case Failure(e) => Left(s"Parse error: ${e.getMessage}") } } } - -object ServerCodec extends ServerCodec -trait ServerCodec extends codec.EventMessageFormats with codec.CommandMessageFormats with BasicJsonProtocol diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index f102cb736..55c38768f 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -8,15 +8,16 @@ package server import java.net.{ SocketTimeoutException, InetAddress, ServerSocket, SocketException } import java.util.concurrent.atomic.AtomicBoolean import sbt.util.Logger +import sbt.protocol._ import scala.collection.mutable private[sbt] sealed trait ServerInstance { def shutdown(): Unit - def publish(event: Event): Unit + def publish(event: EventMessage): Unit } private[sbt] object Server { - def start(host: String, port: Int, onIncommingCommand: Command => Unit, log: Logger): ServerInstance = + def start(host: String, port: Int, onIncommingCommand: CommandMessage => Unit, log: Logger): ServerInstance = new ServerInstance { val lock = new AnyRef {} @@ -36,7 +37,7 @@ private[sbt] object Server { log.info(s"new client connected from: ${socket.getPort}") val connection = new ClientConnection(socket) { - override def onCommand(command: Command): Unit = { + override def onCommand(command: CommandMessage): Unit = { onIncommingCommand(command) } } @@ -55,7 +56,7 @@ private[sbt] object Server { serverThread.start() /** Publish an event to all connected clients */ - def publish(event: Event): Unit = { + def publish(event: EventMessage): Unit = { // TODO do not do this on the calling thread val bytes = Serialization.serialize(event) lock.synchronized { diff --git a/main-command/src/main/scala/sbt/internal/server/protocol.scala b/main-command/src/main/scala/sbt/internal/server/protocol.scala deleted file mode 100644 index 7c16b0fc3..000000000 --- a/main-command/src/main/scala/sbt/internal/server/protocol.scala +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2016 Lightbend Inc. - */ -package sbt -package internal -package server - -/* - * These classes are the protocol for client-server interaction, - * commands can come from the client side, while events are emitted - * from sbt to inform the client of state changes etc. - */ -private[sbt] sealed trait Event - -private[sbt] final case class LogEvent(level: String, message: String) extends Event - -sealed trait Status -private[sbt] final case object Ready extends Status -private[sbt] final case class Processing(command: String, commandQueue: Seq[String]) extends Status - -private[sbt] final case class StatusEvent(status: Status) extends Event -private[sbt] final case class ExecutionEvent(command: String, success: Boolean) extends Event - -private[sbt] sealed trait Command - -private[sbt] final case class Execution(cmd: String) extends Command diff --git a/protocol/src/main/contraband-scala/sbt/protocol/CommandMessage.scala b/protocol/src/main/contraband-scala/sbt/protocol/CommandMessage.scala new file mode 100644 index 000000000..26c102a47 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/CommandMessage.scala @@ -0,0 +1,26 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +/** Message to invoke command. */ +abstract class CommandMessage() extends Serializable { + + + + +override def equals(o: Any): Boolean = o match { + case x: CommandMessage => true + case _ => false +} +override def hashCode: Int = { + 17 +} +override def toString: String = { + "CommandMessage()" +} +} +object CommandMessage { + +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/EventMessage.scala b/protocol/src/main/contraband-scala/sbt/protocol/EventMessage.scala new file mode 100644 index 000000000..c564c7fa0 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/EventMessage.scala @@ -0,0 +1,26 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +/** Message for events. */ +abstract class EventMessage() extends Serializable { + + + + +override def equals(o: Any): Boolean = o match { + case x: EventMessage => true + case _ => false +} +override def hashCode: Int = { + 17 +} +override def toString: String = { + "EventMessage()" +} +} +object EventMessage { + +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/ExecCommand.scala b/protocol/src/main/contraband-scala/sbt/protocol/ExecCommand.scala new file mode 100644 index 000000000..83e32fee4 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/ExecCommand.scala @@ -0,0 +1,33 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +/** Command to execute sbt command. */ +final class ExecCommand private ( + val commandLine: String) extends sbt.protocol.CommandMessage() with Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: ExecCommand => (this.commandLine == x.commandLine) + case _ => false + } + override def hashCode: Int = { + 37 * (17 + commandLine.##) + } + override def toString: String = { + "ExecCommand(" + commandLine + ")" + } + protected[this] def copy(commandLine: String = commandLine): ExecCommand = { + new ExecCommand(commandLine) + } + def withCommandLine(commandLine: String): ExecCommand = { + copy(commandLine = commandLine) + } +} +object ExecCommand { + + def apply(commandLine: String): ExecCommand = new ExecCommand(commandLine) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/ExectionEvent.scala b/protocol/src/main/contraband-scala/sbt/protocol/ExectionEvent.scala new file mode 100644 index 000000000..3a080e64b --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/ExectionEvent.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +/** Executon event. */ +final class ExectionEvent private ( + val success: String, + val commandLine: String) extends Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: ExectionEvent => (this.success == x.success) && (this.commandLine == x.commandLine) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + success.##) + commandLine.##) + } + override def toString: String = { + "ExectionEvent(" + success + ", " + commandLine + ")" + } + protected[this] def copy(success: String = success, commandLine: String = commandLine): ExectionEvent = { + new ExectionEvent(success, commandLine) + } + def withSuccess(success: String): ExectionEvent = { + copy(success = success) + } + def withCommandLine(commandLine: String): ExectionEvent = { + copy(commandLine = commandLine) + } +} +object ExectionEvent { + + def apply(success: String, commandLine: String): ExectionEvent = new ExectionEvent(success, commandLine) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/LogEvent.scala b/protocol/src/main/contraband-scala/sbt/protocol/LogEvent.scala new file mode 100644 index 000000000..79e239e6a --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/LogEvent.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +/** Log event. */ +final class LogEvent private ( + val level: String, + val message: String) extends sbt.protocol.EventMessage() with Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: LogEvent => (this.level == x.level) && (this.message == x.message) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + level.##) + message.##) + } + override def toString: String = { + "LogEvent(" + level + ", " + message + ")" + } + protected[this] def copy(level: String = level, message: String = message): LogEvent = { + new LogEvent(level, message) + } + def withLevel(level: String): LogEvent = { + copy(level = level) + } + def withMessage(message: String): LogEvent = { + copy(message = message) + } +} +object LogEvent { + + def apply(level: String, message: String): LogEvent = new LogEvent(level, message) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/StatusEvent.scala b/protocol/src/main/contraband-scala/sbt/protocol/StatusEvent.scala new file mode 100644 index 000000000..ad3fa6797 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/StatusEvent.scala @@ -0,0 +1,37 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol +/** Status event. */ +final class StatusEvent private ( + val status: String, + val commandQueue: Vector[String]) extends sbt.protocol.EventMessage() with Serializable { + + + + override def equals(o: Any): Boolean = o match { + case x: StatusEvent => (this.status == x.status) && (this.commandQueue == x.commandQueue) + case _ => false + } + override def hashCode: Int = { + 37 * (37 * (17 + status.##) + commandQueue.##) + } + override def toString: String = { + "StatusEvent(" + status + ", " + commandQueue + ")" + } + protected[this] def copy(status: String = status, commandQueue: Vector[String] = commandQueue): StatusEvent = { + new StatusEvent(status, commandQueue) + } + def withStatus(status: String): StatusEvent = { + copy(status = status) + } + def withCommandQueue(commandQueue: Vector[String]): StatusEvent = { + copy(commandQueue = commandQueue) + } +} +object StatusEvent { + + def apply(status: String, commandQueue: Vector[String]): StatusEvent = new StatusEvent(status, commandQueue) +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala new file mode 100644 index 000000000..3a747c4e3 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/CommandMessageFormats.scala @@ -0,0 +1,10 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats => +implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat1[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand]("type") +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala new file mode 100644 index 000000000..f42d3bd96 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/EventMessageFormats.scala @@ -0,0 +1,10 @@ +/** + * This code is generated using sbt-datatype. + */ + +// 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.LogEventFormats with sbt.protocol.codec.StatusEventFormats => +implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat2[sbt.protocol.EventMessage, sbt.protocol.LogEvent, sbt.protocol.StatusEvent]("type") +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/ExecCommandFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/ExecCommandFormats.scala new file mode 100644 index 000000000..ca0561450 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/ExecCommandFormats.scala @@ -0,0 +1,27 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait ExecCommandFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val ExecCommandFormat: JsonFormat[sbt.protocol.ExecCommand] = new JsonFormat[sbt.protocol.ExecCommand] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExecCommand = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val commandLine = unbuilder.readField[String]("commandLine") + unbuilder.endObject() + sbt.protocol.ExecCommand(commandLine) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.ExecCommand, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("commandLine", obj.commandLine) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/ExectionEventFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/ExectionEventFormats.scala new file mode 100644 index 000000000..5ba94d94e --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/ExectionEventFormats.scala @@ -0,0 +1,29 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait ExectionEventFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val ExectionEventFormat: JsonFormat[sbt.protocol.ExectionEvent] = new JsonFormat[sbt.protocol.ExectionEvent] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.ExectionEvent = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val success = unbuilder.readField[String]("success") + val commandLine = unbuilder.readField[String]("commandLine") + unbuilder.endObject() + sbt.protocol.ExectionEvent(success, commandLine) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.ExectionEvent, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("success", obj.success) + builder.addField("commandLine", obj.commandLine) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala new file mode 100644 index 000000000..c8f4aeb13 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/JsonProtocol.scala @@ -0,0 +1,8 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +trait JsonProtocol extends sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.CommandMessageFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.StatusEventFormats with sbt.protocol.codec.EventMessageFormats +object JsonProtocol extends JsonProtocol \ No newline at end of file diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/LogEventFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/LogEventFormats.scala new file mode 100644 index 000000000..861776178 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/LogEventFormats.scala @@ -0,0 +1,29 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait LogEventFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val LogEventFormat: JsonFormat[sbt.protocol.LogEvent] = new JsonFormat[sbt.protocol.LogEvent] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.LogEvent = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val level = unbuilder.readField[String]("level") + val message = unbuilder.readField[String]("message") + unbuilder.endObject() + sbt.protocol.LogEvent(level, message) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.LogEvent, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("level", obj.level) + builder.addField("message", obj.message) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband-scala/sbt/protocol/codec/StatusEventFormats.scala b/protocol/src/main/contraband-scala/sbt/protocol/codec/StatusEventFormats.scala new file mode 100644 index 000000000..0819d1a22 --- /dev/null +++ b/protocol/src/main/contraband-scala/sbt/protocol/codec/StatusEventFormats.scala @@ -0,0 +1,29 @@ +/** + * This code is generated using sbt-datatype. + */ + +// DO NOT EDIT MANUALLY +package sbt.protocol.codec +import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } +trait StatusEventFormats { self: sjsonnew.BasicJsonProtocol => +implicit lazy val StatusEventFormat: JsonFormat[sbt.protocol.StatusEvent] = new JsonFormat[sbt.protocol.StatusEvent] { + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.StatusEvent = { + jsOpt match { + case Some(js) => + unbuilder.beginObject(js) + val status = unbuilder.readField[String]("status") + val commandQueue = unbuilder.readField[Vector[String]]("commandQueue") + unbuilder.endObject() + sbt.protocol.StatusEvent(status, commandQueue) + case None => + deserializationError("Expected JsObject but found None") + } + } + override def write[J](obj: sbt.protocol.StatusEvent, builder: Builder[J]): Unit = { + builder.beginObject() + builder.addField("status", obj.status) + builder.addField("commandQueue", obj.commandQueue) + builder.endObject() + } +} +} diff --git a/protocol/src/main/contraband/server.contra b/protocol/src/main/contraband/server.contra new file mode 100644 index 000000000..9d47dae89 --- /dev/null +++ b/protocol/src/main/contraband/server.contra @@ -0,0 +1,40 @@ +package sbt.protocol +@target(Scala) +@codecPackage("sbt.protocol.codec") +@fullCodec("JsonProtocol") + +## Message to invoke command. +interface CommandMessage { +} + +## Command to execute sbt command. +type ExecCommand implements CommandMessage { + commandLine: String! +} + +## Message for events. +interface EventMessage { +} + +## Log event. +type LogEvent implements EventMessage { + level: String! + message: String! +} + +## Status event. +type StatusEvent implements EventMessage { + status: String! + commandQueue: [String] +} + +# enum Status { +# Ready +# Processing +# } + +## Executon event. +type ExectionEvent { + success: String! + commandLine: String! +} diff --git a/server.md b/server.md new file mode 100644 index 000000000..ddaa9b034 --- /dev/null +++ b/server.md @@ -0,0 +1,8 @@ + + +### ExecCommand + +```json +{ "type": "ExecCommand", "commandLine": "compile" } +``` +