Skip to main content

qubit_http/sse/
done_marker_policy.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//! # Done Marker Policy
11//!
12//! Defines how stream completion markers are recognized.
13//!
14
15use serde::{Deserialize, Serialize};
16use std::fmt;
17use std::str::FromStr;
18
19/// Policy for stream completion marker matching.
20#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
21#[serde(rename_all = "snake_case")]
22pub enum DoneMarkerPolicy {
23    /// Disable done marker recognition.
24    Disabled,
25    /// Use default marker: `[DONE]`.
26    #[default]
27    DefaultDone,
28    /// Use a custom marker string.
29    Custom(String),
30}
31
32impl fmt::Display for DoneMarkerPolicy {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            Self::Disabled => write!(f, "disable"),
36            Self::DefaultDone => write!(f, "default"),
37            Self::Custom(value) => write!(f, "{value}"),
38        }
39    }
40}
41
42impl FromStr for DoneMarkerPolicy {
43    type Err = parse_display::ParseError;
44
45    fn from_str(input: &str) -> Result<Self, Self::Err> {
46        let trimmed = input.trim();
47        if trimmed.eq_ignore_ascii_case("disable") {
48            return Ok(Self::Disabled);
49        }
50        if trimmed.eq_ignore_ascii_case("disabled") {
51            return Ok(Self::Disabled);
52        }
53        if trimmed.eq_ignore_ascii_case("default") {
54            return Ok(Self::DefaultDone);
55        }
56        Ok(Self::Custom(trimmed.to_string()))
57    }
58}
59
60impl DoneMarkerPolicy {
61    /// Returns whether `payload` (trimmed) signals end-of-stream per this policy.
62    ///
63    /// # Parameters
64    /// - `payload`: Typically trimmed SSE `data:` text.
65    ///
66    /// # Returns
67    /// `true` when the stream should stop emitting data chunks (e.g. `[DONE]`).
68    pub fn is_done(&self, payload: &str) -> bool {
69        match self {
70            DoneMarkerPolicy::Disabled => false,
71            DoneMarkerPolicy::DefaultDone => payload.trim() == "[DONE]",
72            DoneMarkerPolicy::Custom(marker) => payload.trim() == marker.trim(),
73        }
74    }
75}