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