package com.sptmobile.link import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json // [impl->REQ-DEVICE-LINK-IROH] /** * Decoded `followNext` envelope. The contract (rust/link-android module doc) * is a closed set: `{"event":"delta","delta":""}` | * `{"event":"timeout"}` | `{"event":"end"}` | * `{"event":"error","message":"..."}`. `followNext` never throws for stream * conditions, so a reader loop handles every outcome through this type; * an unrecognized event decodes as [Error] rather than crashing the loop. */ sealed interface FollowEvent { /** A digest delta arrived; [deltaJson] is the raw delta line. */ data class Delta(val deltaJson: String) : FollowEvent /** No event within `timeoutMs`; poll again. */ data object Timeout : FollowEvent /** Stream ended cleanly (host closed the follow). */ data object End : FollowEvent /** Stream failed; the follow handle is dead. */ data class Error(val message: String) : FollowEvent companion object { @Serializable private data class Envelope( val event: String, val delta: String? = null, val message: String? = null, ) private val json = Json { ignoreUnknownKeys = true } fun decode(envelopeJson: String): FollowEvent { val env = try { json.decodeFromString(envelopeJson) } catch (e: IllegalArgumentException) { return Error("bad follow envelope: ${e.message}") } return when (env.event) { "delta" -> env.delta?.let(::Delta) ?: Error("delta envelope missing delta field") "timeout" -> Timeout "end" -> End "error" -> Error(env.message ?: "unknown stream error") else -> Error("unknown follow event: ${env.event}") } } } }