All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.caesarealabs.rpc4k.runtime.api.C2SEventMessage.kt Maven / Gradle / Ivy

package com.caesarealabs.rpc4k.runtime.api

import com.caesarealabs.rpc4k.runtime.implementation.fastConcat

public sealed interface C2SEventMessage {
    public val event: String
    public data class Subscribe(
        public override val event: String,
        /**
         * Unique identifier for the exact client function that requested this subscription. The client needs this to know where to route events.
         */
        public val listenerId: String,
        /**
         * The data by which the subscription was made. This information is exposed as parameters in the event transformer.
         */
        public val data: ByteArray,
        /**
         * @see EventTarget
         */
        public val target: String?) : com.caesarealabs.rpc4k.runtime.api.C2SEventMessage {
        override fun equals(other: Any?): Boolean {
            return other is com.caesarealabs.rpc4k.runtime.api.C2SEventMessage.Subscribe && event == other.event && listenerId == other.listenerId
                && data.contentEquals(other.data) && target == other.target
        }

        init {
            if (target != null) require(target.isNotEmpty()) { "If the target is specified it must not be blank." }
        }

        override fun hashCode(): Int {
            var result = event.hashCode()
            result = 31 * result + listenerId.hashCode()
            result = 31 * result + data.contentHashCode()
            result = 31 * result + (target?.hashCode() ?: 0)
            return result
        }
    }

    public data class Unsubscribe(override val event: String, val listenerId: String) : C2SEventMessage


    public companion object {
        internal fun fromByteArray(bytes: ByteArray): C2SEventMessage {
            serverRequirement(bytes.isNotEmpty()) { "Event message is empty" }
            val reader = RpcMessageReader(bytes)
            val type = reader.readPart("type").decodeToString()
            val event = reader.readPart("event").decodeToString()
            when (type) {
                "sub" -> {
                    val listenerId = reader.readPart("listener id").decodeToString()
                    val target = reader.readPart("target").decodeToString()
                    val data = reader.readPart("payload", finalPart = true)
                    return Subscribe(
                        event,
                        listenerId,
                        data,
                        target = target.ifEmpty { null })
                }

                "unsub" -> {
                    val listenerId = reader.readPart("listener id", finalPart = true).decodeToString()
                    return Unsubscribe(event, listenerId)
                }

                else -> throw InvalidRpcRequestException("Invalid event message format, the type should be 'sub' or 'unsub' but is '${type}'")
            }
        }

    }

    public fun toByteArray(): ByteArray {
        return when (this) {
            is Subscribe -> "sub".encodeToByteArray()
                .fastConcat(Rpc.ColonCode, event.encodeToByteArray(), listenerId.encodeToByteArray(), (target ?: "").encodeToByteArray(), data)

            is Unsubscribe -> "unsub".encodeToByteArray()
                .fastConcat(Rpc.ColonCode, event.encodeToByteArray(), listenerId.encodeToByteArray())
        }
    }

}


internal class RpcMessageReader(private val bytes: ByteArray) {
    private var i = 0
    fun readPart(partName: String, finalPart: Boolean = false): ByteArray {
        if (i >= bytes.size) throw InvalidRpcRequestException("Invalid event message format, $partName is an empty string")
        val startIndex = i
        while (bytes[i] != Rpc.ColonCode || finalPart) {
            i++
            if (i >= bytes.size) {
                if (finalPart) break
                else throw InvalidRpcRequestException("Invalid event message format, missing $partName terminator ':'")
            }
        }
        // Skip the ':'
        return bytes.copyOfRange(startIndex, i++)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy