/**
Copyright (c) 2025 hashli
eventsource4cj is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
package eventsource4cj.model
import std.collection.ArrayList
import std.math.numeric.*
import stdx.log.*
public struct Message {
static let NEXT_DATA_LINE = '\n'
static let FIELD_LABEL_EVENT = "event"
static let FIELD_LABEL_ID = "id"
static let FIELD_LABEL_RETRY = "retry"
static let FIELD_LABEL_DATA = "data"
static let FIELD_MARK_COLON = ":"
/*
* 事件类型。可选。自定义的任意值
*/
let event: Option<String>
/*
* 事件id。可选。可用于重连时定位上次消息位置
*/
let id: Option<String>
/*
* 重试时间(毫秒)。可选
*/
let retry: Option<UInt16>
/*
* 注释。可选
*/
let comment: Option<String>
/*
* 数据。
*/
let dataList: ArrayList<String>
/**
* 拓展属性
*/
let ext = MessageExtend()
protected init(event: Option<String>, id: Option<String>, retry: Option<UInt16>, comment: Option<String>,
dataList: ArrayList<String>, tag: Option<String>) {
this.event = event
this.id = id
this.retry = retry
this.comment = comment
this.dataList = dataList
this.ext.tag = tag
}
public prop extention: MessageExtend {
get() {
return this.ext
}
}
public prop eventType: Option<String> {
get() {
return this.event
}
}
public prop msgId: Option<String> {
get() {
return this.id
}
}
public prop dataLines: ArrayList<String> {
get() {
return this.dataList
}
}
public prop data: Option<String> {
get() {
if (this.dataList.isEmpty()) {
return Option.None
} else {
return this.dataList[0]
}
}
}
public prop retryInMills: Option<UInt16> {
get() {
return this.retry
}
}
public func toMessageString(): String {
let body = StringBuilder()
// 处理注释
if (this.comment.isSome()) {
body.append(FIELD_MARK_COLON)
body.append(this.comment.getOrThrow())
body.append(NEXT_DATA_LINE)
}
// 处理消息体
if (this.event.isSome()) {
body.append(FIELD_LABEL_EVENT)
body.append(FIELD_MARK_COLON)
body.append(this.event.getOrThrow())
body.append(NEXT_DATA_LINE)
}
if (this.id.isSome()) {
body.append(FIELD_LABEL_ID)
body.append(FIELD_MARK_COLON)
body.append(this.id.getOrThrow())
body.append(NEXT_DATA_LINE)
}
if (this.retry.isSome()) {
body.append(FIELD_LABEL_RETRY)
body.append(FIELD_MARK_COLON)
body.append("${this.retry.getOrThrow()}")
body.append(NEXT_DATA_LINE)
}
for (data in dataList) {
body.append(FIELD_LABEL_DATA)
body.append(FIELD_MARK_COLON)
body.append(data)
body.append(NEXT_DATA_LINE)
}
return body.toString() + NEXT_DATA_LINE
}
}
public class MessageExtend {
var tag: Option<String>
public init() {
this.tag = Option.None
}
public prop messageTag: Option<String> {
get() {
return this.tag
}
}
}
public class MessageBuilder {
static let log: Logger = getGlobalLogger()
private var _tag: Option<String>
private var _event: Option<String>
private var _id: Option<String>
private var _retry: Option<UInt16>
private var _comment: Option<String>
private let _dataList: ArrayList<String>
public init() {
this._tag = Option.None
this._event = Option.None
this._id = Option.None
this._retry = Option.None
this._comment = Option.None
this._dataList = ArrayList<String>()
}
public func tag(tag: String): MessageBuilder {
this._tag = tag
return this
}
public func event(envent: String): MessageBuilder {
this._event = envent
return this
}
public func id(id: String): MessageBuilder {
this._id = id
return this
}
public func retry(retry: UInt16): MessageBuilder {
this._retry = retry
return this
}
public func comment(comment: String): MessageBuilder {
this._comment = comment
return this
}
public func data(data: String): MessageBuilder {
this._dataList.add(data)
return this
}
public func build(): Message {
return Message(_event, _id, _retry, _comment, _dataList, _tag)
}
public static func resolve(messageBody: Array<UInt8>): Message {
let mb = MessageBuilder()
for (line in String.fromUtf8(messageBody).lines()) {
if (line.startsWith(Message.FIELD_MARK_COLON)) {
mb.comment(getValue(line))
} else if (line.startsWith(Message.FIELD_LABEL_EVENT)) {
mb.event(getValue(line))
} else if (line.startsWith(Message.FIELD_LABEL_ID)) {
mb.id(getValue(line))
} else if (line.startsWith(Message.FIELD_LABEL_RETRY)) {
let retryValue = getValue(line)
if (retryValue.size > 0) {
mb.retry(Decimal.parse(retryValue).toUInt16())
}
} else if (line.startsWith(Message.FIELD_LABEL_DATA)) {
mb.data(getValue(line))
} else if (line == "") {
} else {
log.warn("未识别的数据行:${line}")
}
}
return mb.build()
}
static func getValue(messageLine: String): String {
return messageLine.split(Message.FIELD_MARK_COLON, 2)[1]
}
}