package com.suno.android.common_networking.sse import javax.inject.Inject private const val ID_LINE_PREFIX = "id:" private const val DATA_LINE_PREFIX = "data:" private const val EVENT_LINE_PREFIX = "event:" private const val RETRY_LINE_PREFIX = "retry:" private const val COMMENT_PREFIX = ":" /** * Parses Server-Sent Events lines according to the WHATWG SSE specification. * https://html.spec.whatwg.org/multipage/server-sent-events.html */ class SseLineParser @Inject constructor() { /** * Parses a single SSE line according to the specification. */ fun parseLine( rawLine: String, ): ParsedLine = when { rawLine.startsWith(ID_LINE_PREFIX) -> ParsedLine.Id(rawLine.substringAfter(ID_LINE_PREFIX).trim()) rawLine.startsWith(DATA_LINE_PREFIX) -> ParsedLine.Data(rawLine.substringAfter(DATA_LINE_PREFIX).trim()) rawLine.startsWith(EVENT_LINE_PREFIX) -> ParsedLine.Event(rawLine.substringAfter(EVENT_LINE_PREFIX).trim()) rawLine.startsWith(RETRY_LINE_PREFIX) -> { val retryValue = rawLine.substringAfter(RETRY_LINE_PREFIX).trim() val retryMs = retryValue.toLongOrNull() ParsedLine.Retry(retryMs) } rawLine.startsWith(COMMENT_PREFIX) -> ParsedLine.Comment(rawLine) rawLine.isBlank() -> ParsedLine.EndOfEvent(rawLine) else -> ParsedLine.Other(rawLine) } sealed class ParsedLine { /** * Event ID for reconnection via lastEventId param. */ data class Id( val id: String, ) : ParsedLine() /** * Event data. Multiple data lines are joined with newlines per SSE spec. */ data class Data( val data: String, ) : ParsedLine() /** * Event type. */ data class Event( val eventType: String, ) : ParsedLine() /** * Reconnection time in milliseconds. */ data class Retry( val retryMs: Long?, ) : ParsedLine() /** * Comment line starting with ':'. Ignored per SSE spec. */ data class Comment( val rawLine: String, ) : ParsedLine() /** * Blank line indicating end of event. Triggers event dispatch per SSE spec. */ data class EndOfEvent( val rawLine: String, ) : ParsedLine() /** * Unrecognized line format. Ignored per SSE spec. */ data class Other( val rawLine: String, ) : ParsedLine() } }