Using sbt-datatype + sjson-new for JSON serialization

This commit is contained in:
Eugene Yokota 2016-10-27 09:27:41 -04:00
parent e7456b5653
commit 18233ace05
9 changed files with 297 additions and 55 deletions

View File

@ -176,11 +176,13 @@ lazy val actionsProj = (project in file("main-actions")).
// General command support and core commands not specific to a build system
lazy val commandProj = (project in file("main-command")).
enablePlugins(DatatypePlugin, JsonCodecPlugin).
settings(
testedBaseSettings,
name := "Command",
libraryDependencies ++= Seq(launcherInterface, compilerInterface,
sbtIO, utilLogging, utilCompletion, compilerClasspath, json4s, json4sNative) // to transitively get json4s)
sbtIO, utilLogging, utilCompletion, compilerClasspath, sjsonNewScalaJson),
sourceManaged in (Compile, generateDatatypes) := baseDirectory.value / "src" / "main" / "datatype-scala"
)
// Fixes scope=Scope for Setting (core defined in collectionProj) to define the settings system used in build definitions

View File

@ -0,0 +1,39 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server
final class CommandMessage(
val `type`: String,
val commandLine: Option[String]) extends Serializable {
def this(`type`: String) = this(`type`, None)
override def equals(o: Any): Boolean = o match {
case x: CommandMessage => (this.`type` == x.`type`) && (this.commandLine == x.commandLine)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + `type`.##) + commandLine.##)
}
override def toString: String = {
"CommandMessage(" + `type` + ", " + commandLine + ")"
}
def copy(`type`: String): CommandMessage = {
new CommandMessage(`type`, commandLine)
}
def copy(`type`: String = `type`, commandLine: Option[String] = commandLine): CommandMessage = {
new CommandMessage(`type`, commandLine)
}
def withType(`type`: String): CommandMessage = {
copy(`type` = `type`)
}
def withCommandLine(commandLine: Option[String]): CommandMessage = {
copy(commandLine = commandLine)
}
}
object CommandMessage {
def apply(`type`: String): CommandMessage = new CommandMessage(`type`, None)
def apply(`type`: String, commandLine: Option[String]): CommandMessage = new CommandMessage(`type`, commandLine)
}

View File

@ -0,0 +1,59 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server
final class EventMessage(
val `type`: String,
val status: Option[String],
val commandQueue: Vector[String],
val level: Option[String],
val message: Option[String],
val success: Option[Boolean],
val commandLine: Option[String]) extends Serializable {
def this(`type`: String) = this(`type`, None, Vector(), None, None, None, None)
override def equals(o: Any): Boolean = o match {
case x: EventMessage => (this.`type` == x.`type`) && (this.status == x.status) && (this.commandQueue == x.commandQueue) && (this.level == x.level) && (this.message == x.message) && (this.success == x.success) && (this.commandLine == x.commandLine)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (37 * (37 * (17 + `type`.##) + status.##) + commandQueue.##) + level.##) + message.##) + success.##) + commandLine.##)
}
override def toString: String = {
"EventMessage(" + `type` + ", " + status + ", " + commandQueue + ", " + level + ", " + message + ", " + success + ", " + commandLine + ")"
}
def copy(`type`: String): EventMessage = {
new EventMessage(`type`, status, commandQueue, level, message, success, commandLine)
}
def copy(`type`: String = `type`, status: Option[String] = status, commandQueue: Vector[String] = commandQueue, level: Option[String] = level, message: Option[String] = message, success: Option[Boolean] = success, commandLine: Option[String] = commandLine): EventMessage = {
new EventMessage(`type`, status, commandQueue, level, message, success, commandLine)
}
def withType(`type`: String): EventMessage = {
copy(`type` = `type`)
}
def withStatus(status: Option[String]): EventMessage = {
copy(status = status)
}
def withCommandQueue(commandQueue: Vector[String]): EventMessage = {
copy(commandQueue = commandQueue)
}
def withLevel(level: Option[String]): EventMessage = {
copy(level = level)
}
def withMessage(message: Option[String]): EventMessage = {
copy(message = message)
}
def withSuccess(success: Option[Boolean]): EventMessage = {
copy(success = success)
}
def withCommandLine(commandLine: Option[String]): EventMessage = {
copy(commandLine = commandLine)
}
}
object EventMessage {
def apply(`type`: String): EventMessage = new EventMessage(`type`, None, Vector(), None, None, None, None)
def apply(`type`: String, status: Option[String], commandQueue: Vector[String], level: Option[String], message: Option[String], success: Option[Boolean], commandLine: Option[String]): EventMessage = new EventMessage(`type`, status, commandQueue, level, message, success, commandLine)
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.internal.server.CommandMessage] = new JsonFormat[sbt.internal.server.CommandMessage] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.server.CommandMessage = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val `type` = unbuilder.readField[String]("type")
val commandLine = unbuilder.readField[Option[String]]("commandLine")
unbuilder.endObject()
new sbt.internal.server.CommandMessage(`type`, commandLine)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.server.CommandMessage, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("type", obj.`type`)
builder.addField("commandLine", obj.commandLine)
builder.endObject()
}
}
}

View File

@ -0,0 +1,39 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.internal.server.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.internal.server.EventMessage] = new JsonFormat[sbt.internal.server.EventMessage] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.server.EventMessage = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val `type` = unbuilder.readField[String]("type")
val status = unbuilder.readField[Option[String]]("status")
val commandQueue = unbuilder.readField[Vector[String]]("commandQueue")
val level = unbuilder.readField[Option[String]]("level")
val message = unbuilder.readField[Option[String]]("message")
val success = unbuilder.readField[Option[Boolean]]("success")
val commandLine = unbuilder.readField[Option[String]]("commandLine")
unbuilder.endObject()
new sbt.internal.server.EventMessage(`type`, status, commandQueue, level, message, success, commandLine)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.server.EventMessage, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("type", obj.`type`)
builder.addField("status", obj.status)
builder.addField("commandQueue", obj.commandQueue)
builder.addField("level", obj.level)
builder.addField("message", obj.message)
builder.addField("success", obj.success)
builder.addField("commandLine", obj.commandLine)
builder.endObject()
}
}
}

View File

@ -0,0 +1,73 @@
{
"codecNamespace": "sbt.internal.server.codec",
"types": [
{
"name": "CommandMessage",
"namespace": "sbt.internal.server",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "type",
"type": "String",
"since": "0.0.0"
},
{
"name": "commandLine",
"type": "String?",
"default": "None",
"since": "0.1.0"
}
]
},
{
"name": "EventMessage",
"namespace": "sbt.internal.server",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "type",
"type": "String",
"since": "0.0.0"
},
{
"name": "status",
"type": "String?",
"default": "None",
"since": "0.1.0"
},
{
"name": "commandQueue",
"type": "String*",
"default": "Vector()",
"since": "0.1.0"
},
{
"name": "level",
"type": "String?",
"default": "None",
"since": "0.1.0"
},
{
"name": "message",
"type": "String?",
"default": "None",
"since": "0.1.0"
},
{
"name": "success",
"type": "boolean?",
"default": "None",
"since": "0.1.0"
},
{
"name": "commandLine",
"type": "String?",
"default": "None",
"since": "0.1.0"
}
]
}
]
}

View File

@ -5,66 +5,68 @@ package sbt
package internal
package server
import org.json4s.JsonAST.{ JArray, JString }
import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods._
import org.json4s.ParserUtil.ParseException
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 }
object Serialization {
def serialize(event: Event): Array[Byte] = {
compact(render(toJson(event))).getBytes("UTF-8")
}
def toJson(event: Event): JObject = event match {
case LogEvent(level, message) =>
JObject(
"type" -> JString("log_event"),
"level" -> JString(level),
"message" -> JString(message)
)
case StatusEvent(Ready) =>
JObject(
"type" -> JString("status_event"),
"status" -> JString("ready"),
"command_queue" -> JArray(List.empty)
)
case StatusEvent(Processing(command, commandQueue)) =>
JObject(
"type" -> JString("status_event"),
"status" -> JString("processing"),
"command_queue" -> JArray(commandQueue.map(JString).toList)
)
case ExecutionEvent(command, status) =>
JObject(
"type" -> JString("execution_event"),
"command" -> JString(command),
"success" -> JBool(status)
)
}
def serialize(event: Event): Array[Byte] =
{
import ServerCodec._
val msg = toMessage(event)
val json: JValue = Converter.toJson[EventMessage](msg).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] =
try {
val json = parse(new String(bytes.toArray, "UTF-8"))
implicit val formats = DefaultFormats
(json \ "type").toOption match {
case Some(JString("exec")) =>
(json \ "command_line").toOption match {
case Some(JString(cmd)) => Right(Execution(cmd))
case _ => Left("Missing or invalid command_line field")
{
val buffer = ByteBuffer.wrap(bytes.toArray)
Parser.parseFromByteBuffer(buffer) match {
case Success(json) =>
import ServerCodec._
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 Some(cmd) => Left(s"Unknown command type $cmd")
case None => Left("Invalid command, missing type field")
case Failure(e) =>
Left(s"Parse error: ${e.getMessage}")
}
} catch {
case e: ParseException => Left(s"Parse error: ${e.getMessage}")
}
}
object ServerCodec extends ServerCodec
trait ServerCodec extends codec.EventMessageFormats with codec.CommandMessageFormats with BasicJsonProtocol

View File

@ -37,9 +37,7 @@ object Dependencies {
lazy val compilerClasspath = "org.scala-sbt" %% "zinc-classpath" % zincVersion
lazy val compilerApiInfo = "org.scala-sbt" %% "zinc-apiinfo" % zincVersion
lazy val compilerIvyIntegration = "org.scala-sbt" %% "zinc-ivy-integration" % zincVersion
lazy val json4s = "org.json4s" %% "json4s" % "3.2.10"
lazy val json4sNative = "org.json4s" %% "json4s-native" % "3.2.10"
lazy val sjsonNewScalaJson = "com.eed3si9n" %% "sjson-new-scalajson" % "0.4.2"
lazy val scalaCheck = "org.scalacheck" %% "scalacheck" % "1.11.4"
lazy val specs2 = "org.specs2" %% "specs2" % "2.3.11"

1
project/datatype.sbt Normal file
View File

@ -0,0 +1 @@
addSbtPlugin("org.scala-sbt" % "sbt-datatype" % "0.2.6")