#![forbid(missing_docs)]
#![forbid(missing_debug_implementations)]
#[cfg(feature = "axum")]
pub mod axum;
#[cfg(feature = "rocket")]
pub mod rocket;
#[cfg(feature = "warp")]
pub mod warp;
pub mod execute_script;
pub mod patch_elements;
pub mod patch_signals;
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
#[expect(unused)]
struct ReadmeDoctests;
pub mod consts;
pub mod prelude {
pub use crate::{
DatastarEvent,
consts::{ElementPatchMode, Namespace},
execute_script::ExecuteScript,
patch_elements::PatchElements,
patch_signals::PatchSignals,
};
}
use core::{
fmt::{self, Display},
time::Duration,
};
pub(crate) trait DatalineWriter {
fn write_dataline(&mut self, args: fmt::Arguments<'_>) -> fmt::Result;
}
impl DatalineWriter for Vec<String> {
fn write_dataline(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
self.push(args.to_string());
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DatastarEvent {
pub event: consts::EventType,
pub id: Option<String>,
pub retry: Duration,
pub data: Vec<String>,
}
impl DatastarEvent {
pub fn new(
event: consts::EventType,
data: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
Self {
event,
id: None,
retry: Duration::from_millis(consts::DEFAULT_SSE_RETRY_DURATION),
data: data.into_iter().map(Into::into).collect(),
}
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn retry(mut self, retry: Duration) -> Self {
self.retry = retry;
self
}
}
impl Display for DatastarEvent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "event: {}", self.event.as_str())?;
if let Some(id) = &self.id {
write!(f, "\nid: {id}")?;
}
let millis = self.retry.as_millis();
if millis != consts::DEFAULT_SSE_RETRY_DURATION as u128 {
write!(f, "\nretry: {millis}")?;
}
for line in &self.data {
write!(f, "\ndata: {line}")?;
}
write!(f, "\n\n")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use {super::*, crate::consts::EventType};
#[test]
fn formats_default_sse_metadata() {
let event = DatastarEvent {
event: EventType::PatchSignals,
id: None,
retry: Duration::from_millis(consts::DEFAULT_SSE_RETRY_DURATION),
data: vec!["signals {count: 1}".into()],
};
assert_eq!(
event.to_string(),
"event: datastar-patch-signals\ndata: signals {count: 1}\n\n"
);
}
#[test]
fn formats_custom_sse_metadata_and_multiline_data() {
let event = DatastarEvent {
event: EventType::PatchElements,
id: Some("event-1".into()),
retry: Duration::from_millis(2_500),
data: vec![
"elements <div>one</div>".into(),
"elements <div>two</div>".into(),
],
};
assert_eq!(
event.to_string(),
concat!(
"event: datastar-patch-elements\n",
"id: event-1\n",
"retry: 2500\n",
"data: elements <div>one</div>\n",
"data: elements <div>two</div>\n\n",
)
);
}
}