Skip to main content

qubit_http/sse/
sse_event.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! # SSE event record
11//!
12//! One dispatch after frame reassembly (`data:` lines joined with `\n`).
13//!
14
15use serde::de::DeserializeOwned;
16
17use super::SseJsonMode;
18use crate::{HttpError, HttpResult};
19
20/// One Server-Sent Events dispatch after frame reassembly (`data:` lines joined with `\n`).
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct SseEvent {
23    /// `event:` field if present.
24    pub event: Option<String>,
25    /// Concatenated `data:` payload (newline-separated if multiple `data` lines).
26    pub data: String,
27    /// `id:` field if present.
28    pub id: Option<String>,
29    /// Parsed `retry:` milliseconds hint if valid.
30    pub retry: Option<u64>,
31}
32
33impl SseEvent {
34    /// Decodes the current event's `data` payload as JSON.
35    ///
36    /// # Type parameters
37    /// - `T`: Target type deserialized from [`SseEvent::data`].
38    ///
39    /// # Returns
40    /// `Ok(T)` when `data` is valid JSON for `T`.
41    ///
42    /// # Errors
43    /// Returns [`HttpError::sse_decode`] when JSON parsing fails.
44    /// The error message includes optional `event` and `id` context.
45    pub fn decode_json<T>(&self) -> HttpResult<T>
46    where
47        T: DeserializeOwned,
48    {
49        serde_json::from_str::<T>(&self.data).map_err(|error| {
50            HttpError::sse_decode(format!(
51                "Failed to decode SSE event data as JSON (event={:?}, id={:?}): {}",
52                self.event, self.id, error
53            ))
54        })
55    }
56
57    /// Decodes the current event's `data` payload as JSON with configurable strictness.
58    ///
59    /// # Parameters
60    /// - `mode`: JSON decoding strictness.
61    ///
62    /// # Type parameters
63    /// - `T`: Target type deserialized from [`SseEvent::data`].
64    ///
65    /// # Returns
66    /// - `Ok(Some(T))` when `data` is valid JSON for `T`.
67    /// - `Ok(None)` in lenient mode when JSON parsing fails.
68    ///
69    /// # Errors
70    /// Returns [`HttpError::sse_decode`] in strict mode when JSON parsing fails.
71    pub fn decode_json_with_mode<T>(&self, mode: SseJsonMode) -> HttpResult<Option<T>>
72    where
73        T: DeserializeOwned,
74    {
75        match mode {
76            SseJsonMode::Strict => self.decode_json::<T>().map(Some),
77            SseJsonMode::Lenient => match serde_json::from_str::<T>(&self.data) {
78                Ok(value) => Ok(Some(value)),
79                Err(error) => {
80                    tracing::debug!(
81                        "Skipping malformed SSE event JSON in lenient mode (event={:?}, id={:?}): {}",
82                        self.event,
83                        self.id,
84                        error
85                    );
86                    Ok(None)
87                }
88            },
89        }
90    }
91}