This repository has been archived by the owner on Mar 5, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMessageCompanions.scala
148 lines (126 loc) · 5.23 KB
/
MessageCompanions.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package com.dhpcs.jsonrpc
import com.dhpcs.jsonrpc.JsonRpcMessage._
import play.api.libs.json._
import scala.language.existentials
import scala.reflect.ClassTag
trait CommandCompanion[A] {
private[this] lazy val (methodReads, classWrites) = CommandFormats
protected[this] val CommandFormats: (
Map[String, Reads[_ <: A]],
Map[Class[_], (String, OWrites[_ <: A])]
)
def read(jsonRpcRequestMessage: JsonRpcRequestMessage): JsResult[_ <: A] =
methodReads.get(jsonRpcRequestMessage.method) match {
case None => JsError(s"unknown method ${jsonRpcRequestMessage.method}")
case Some(reads) =>
jsonRpcRequestMessage.params match {
case NoParams => JsError("command parameters must be given")
case ArrayParams(_) => JsError("command parameters must be named")
case ObjectParams(value) =>
reads.reads(value) match {
// We do this just to reset the path in the success case.
case JsError(invalid) => JsError(invalid)
case JsSuccess(valid, _) => JsSuccess(valid)
}
}
}
def write[B <: A](command: B, id: CorrelationId): JsonRpcRequestMessage = {
val (method, writes) =
classWrites.getOrElse(command.getClass,
throw new IllegalArgumentException(
s"No format found for ${command.getClass}"))
val bWrites = writes.asInstanceOf[OWrites[B]]
JsonRpcRequestMessage(method, bWrites.writes(command), id)
}
}
trait ResponseCompanion[A] {
private[this] lazy val (methodReads, classWrites) = ResponseFormats
protected[this] val ResponseFormats: (
Map[String, Reads[_ <: A]],
Map[Class[_], (String, Writes[_ <: A])]
)
def read(jsonRpcResponseSuccessMessage: JsonRpcResponseSuccessMessage,
method: String): JsResult[_ <: A] =
methodReads(method).reads(jsonRpcResponseSuccessMessage.result) match {
// We do this just to reset the path in the success case.
case JsError(invalid) => JsError(invalid)
case JsSuccess(valid, _) => JsSuccess(valid)
}
def write[B <: A](response: B,
id: CorrelationId): JsonRpcResponseSuccessMessage = {
val (_, writes) =
classWrites.getOrElse(response.getClass,
throw new IllegalArgumentException(
s"No format found for ${response.getClass}"))
val bWrites = writes.asInstanceOf[Writes[B]]
JsonRpcResponseSuccessMessage(bWrites.writes(response), id)
}
}
trait NotificationCompanion[A] {
private[this] lazy val (methodReads, classWrites) = NotificationFormats
protected[this] val NotificationFormats: (Map[String, Reads[_ <: A]],
Map[Class[_],
(String, OWrites[_ <: A])])
def read(jsonRpcNotificationMessage: JsonRpcNotificationMessage)
: JsResult[_ <: A] =
methodReads.get(jsonRpcNotificationMessage.method) match {
case None =>
JsError(s"unknown method ${jsonRpcNotificationMessage.method}")
case Some(reads) =>
jsonRpcNotificationMessage.params match {
case NoParams => JsError("notification parameters must be given")
case ArrayParams(_) =>
JsError("notification parameters must be named")
case ObjectParams(value) =>
reads.reads(value) match {
// We do this just to reset the path in the success case.
case JsError(invalid) => JsError(invalid)
case JsSuccess(valid, _) => JsSuccess(valid)
}
}
}
def write[B <: A](notification: B): JsonRpcNotificationMessage = {
val (method, writes) =
classWrites.getOrElse(notification.getClass,
throw new IllegalArgumentException(
s"No format found for ${notification.getClass}"))
val bWrites = writes.asInstanceOf[OWrites[B]]
JsonRpcNotificationMessage(method, bWrites.writes(notification))
}
}
object Message {
implicit class MessageFormat[A: ClassTag](
methodAndFormat: (String, Format[A])) {
private[Message] val classTag = implicitly[ClassTag[A]]
private[Message] val (method, format) = methodAndFormat
}
object MessageFormats {
def apply[A, W[_] <: Writes[_]](messageFormats: MessageFormat[_ <: A]*)
: (Map[String, Reads[_ <: A]], Map[Class[_], (String, W[_ <: A])]) = {
val methods = messageFormats.map(_.method)
require(
methods == methods.distinct,
"Duplicate methods: " + methods.mkString(", ")
)
val classes = messageFormats.map(_.classTag.runtimeClass)
require(
classes == classes.distinct,
"Duplicate classes: " + classes.mkString(", ")
)
val reads = messageFormats.map(
messageFormat =>
messageFormat.method ->
messageFormat.format)
val writes = messageFormats.map(
messageFormat =>
messageFormat.classTag.runtimeClass ->
(messageFormat.method -> messageFormat.format
.asInstanceOf[W[_ <: A]]))
(reads.toMap, writes.toMap)
}
}
def objectFormat[A](o: A): OFormat[A] = OFormat(
_.validate[JsObject].map(_ => o),
(_: A) => JsObject.empty
)
}