Skip to main content

qubit_http/sse/
sse_event.rs

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