qubit_http/sse/sse_message.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 message record
11//!
12//! One EventSource-style message dispatch after frame reassembly.
13//!
14
15use serde::de::DeserializeOwned;
16
17use super::SseJsonMode;
18use crate::{
19 HttpError,
20 HttpResult,
21};
22
23/// One EventSource-style message dispatch after `data:` line reassembly.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct SseMessage {
26 /// `event:` field if present; callers may treat `None` as the default `message` type.
27 pub event: Option<String>,
28 /// Concatenated `data:` payload.
29 pub data: String,
30 /// Last event id after applying this frame and any prior control-only `id:` frames.
31 pub last_event_id: Option<String>,
32}
33
34impl SseMessage {
35 /// Decodes the current message's `data` payload as JSON.
36 ///
37 /// # Type parameters
38 /// - `T`: Target type deserialized from [`SseMessage::data`].
39 ///
40 /// # Returns
41 /// `Ok(T)` when `data` is valid JSON for `T`.
42 ///
43 /// # Errors
44 /// Returns [`HttpError::sse_decode`] when JSON parsing fails.
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 message data as JSON (event={:?}, last_event_id={:?}): {}",
52 self.event, self.last_event_id, error
53 ))
54 })
55 }
56
57 /// Decodes the current message'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 [`SseMessage::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 message JSON in lenient mode (event={:?}, last_event_id={:?}): {}",
82 self.event,
83 self.last_event_id,
84 error
85 );
86 Ok(None)
87 }
88 },
89 }
90 }
91}