-
Notifications
You must be signed in to change notification settings - Fork 4
/
Domains.scala
242 lines (196 loc) · 7.96 KB
/
Domains.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
* Copyright 2021 Hossein Naderi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package lepus.protocol.domains
import java.time.Instant
import java.util.UUID
trait TaggedOpaqueComp[U, T <: U](using ev: U =:= T) {
def apply(u: U): T = ev(u)
def lift[F[_]](f: F[U]): F[T] = ev.liftCo(f)
}
opaque type ChannelNumber <: Short = Short
object ChannelNumber extends TaggedOpaqueComp[Short, ChannelNumber]
opaque type ShortString <: String = String
object ShortString extends Literally[String, ShortString] {
inline def apply(t: String): ShortString = ${ build('t) }
def from(uuid: UUID): ShortString = uuid.toString()
def from(long: Long): ShortString = long.toString()
private[lepus] inline def unsafe(str: String): ShortString = str
def empty: ShortString = ""
def from(str: String): Either[String, ShortString] =
Either.cond(
str.length <= 255,
str,
s"Maximum size for short strings is 255 characters! cannot create short string with length of ${str.length}"
)
}
opaque type LongString <: String = String
private inline val LongStringSize = 4294967296L // 2 ^ 32
object LongString extends Literally[String, LongString] {
inline def apply(t: String): LongString = ${ build('t) }
def empty: LongString = ""
def from(str: String): Either[String, LongString] =
Either.cond(
str.length <= LongStringSize,
str,
s"Maximum size for long strings is $LongStringSize characters! cannot create short string with length of ${str.length}"
)
}
opaque type Timestamp <: Long = Long
object Timestamp {
def apply(t: Long): Timestamp = t
extension (t: Timestamp) {
def toInstant: Instant = Instant.ofEpochMilli(t)
}
def from(instant: Instant): Timestamp = instant.toEpochMilli()
}
final case class Decimal(scale: Byte, value: Int)
type FieldData =
ShortString | LongString | Boolean | Byte | Short | Int | Long | Float |
Double | Decimal | Timestamp | FieldTable
final case class FieldTable(values: Map[ShortString, FieldData])
extends AnyVal {
def get(key: ShortString): Option[FieldData] = values.get(key)
def updated(key: ShortString, value: FieldData): FieldTable = FieldTable(
values.updated(key, value)
)
def updated(key: ShortString, value: Option[FieldData]): FieldTable =
value.fold(this)(this.updated(key, _))
}
object FieldTable {
def apply(entries: (ShortString, FieldData)*): FieldTable = new FieldTable(
entries.toMap
)
val empty: FieldTable = FieldTable(Map.empty)
}
opaque type ClassId <: Short = Short
object ClassId extends TaggedOpaqueComp[Short, ClassId]
/** Identifier for the consumer, valid within the current c hannel.
*/
opaque type ConsumerTag <: ShortString = ShortString
object ConsumerTag extends TaggedOpaqueComp[ShortString, ConsumerTag] {
inline def of(inline t: String): ConsumerTag = ShortString(t)
def empty: ConsumerTag = ShortString.empty
def from(t: String): Either[String, ConsumerTag] = ShortString.from(t)
}
/** The server-assigned and channel-specific delivery tag
*/
opaque type DeliveryTag <: Long = Long
object DeliveryTag extends TaggedOpaqueComp[Long, DeliveryTag]
private val namePattern = "^[a-zA-Z0-9-_.:]*$".r
private def validateName(name: String): Either[String, String] =
Either.cond(namePattern.matches(name), name, "Invalid name!")
private def validateNameSize(name: String): Either[String, String] =
Either.cond(
name.length <= 127,
name,
"Maximum name length is 127 characters!"
)
/** The exchange name is a client-selected string that identifies the exchange
* for publish methods.
*/
opaque type ExchangeName <: ShortString = String
object ExchangeName extends Literally[String, ExchangeName] {
val default: ExchangeName = ""
inline def apply(t: String): ExchangeName = ${ build('t) }
def from(name: String): Either[String, ExchangeName] =
validateName(name).flatMap(validateNameSize)
}
opaque type MethodId <: Short = Short
object MethodId extends TaggedOpaqueComp[Short, MethodId]
/** If this field is set the server does not expect acknowledgements for
* messages. That is, when a message is delivered to the client the server
* assumes the delivery will succeed and immediately dequeues it. This
* functionality may increase performance but at the cost of reliability.
* Messages can get lost if a client dies before they are delivered to the
* application.
*/
type NoAck = Boolean
/** If the no-local field is set the server will not send messages to the
* connection that published them.
*/
type NoLocal = Boolean
/** If set, the server will not respond to the method. The client should not
* wait for a reply method. If the server could not complete the method it will
* raise a channel or connection exception.
*/
type NoWait = Boolean
/** Unconstrained.
*/
opaque type Path <: ShortString = String
object Path extends Literally[String, Path] {
inline def apply(t: String): Path = ${ build('t) }
def from(str: String): Either[String, Path] =
Either.cond(str.length <= 127, str, "Maximum length is 127 characters!")
}
/** This table provides a set of peer properties, used for identification,
* debugging, and general information.
*/
type PeerProperties = FieldTable
/** The queue name identifies the queue within the vhost. In methods where the
* queue name may be blank, and that has no specific significance, this refers
* to the 'current' queue for the channel, meaning the last queue that the
* client declared on the channel. If the client did not declare a queue, and
* the method needs a queue name, this will result in a 502 (syntax error)
* channel exception.
*/
opaque type QueueName <: ShortString = String
object QueueName extends Literally[String, QueueName] {
val autoGen: QueueName = ""
inline def apply(t: String): QueueName = ${ build('t) }
def from(name: String): Either[String, QueueName] =
validateName(name).flatMap(validateNameSize)
}
/** This indicates that the message has been previously delivered to this or
* another client.
*/
type Redelivered = Boolean
/** The number of messages in the queue, which will be zero for newly-declared
* queues. This is the number of messages present in the queue, and committed
* if the channel on which they were published is transacted, that are not
* waiting acknowledgement.
*/
opaque type MessageCount <: Long = Long
object MessageCount extends Literally[Long, MessageCount] {
inline def apply(t: Long): MessageCount = ${ build('t) }
def from(count: Long): Either[String, MessageCount] =
Either.cond(count >= 0, count, "Count cannot be negative!")
}
/** The localised reply text. This text can be logged as an aid to resolving
* issues.
*/
type ReplyText = ShortString
enum DeliveryMode(val value: Byte) {
case NonPersistent extends DeliveryMode(1)
case Persistent extends DeliveryMode(2)
}
type Priority = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
object Priority extends Literally[Int, Priority] {
inline def apply(t: Int): Priority = ${ build('t) }
def from(b: Int): Either[String, Priority] = b match {
case p: Priority => Right(p)
case _ => Left("Valid priorities are 0-9")
}
}
opaque type ExchangeType <: ShortString = ShortString
object ExchangeType extends Literally[String, ExchangeType] {
inline def apply(t: String): ExchangeType = ${ build('t) }
override def from(str: String): Either[String, ExchangeType] =
ShortString.from(str)
val Direct: ExchangeType = "direct"
val Topic: ExchangeType = "topic"
val Fanout: ExchangeType = "fanout"
val Headers: ExchangeType = "headers"
}