use {
crate::{
DatalineWriter, DatastarEvent,
consts::{self, ElementPatchMode, Namespace},
},
core::{fmt, time::Duration},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PatchElements {
pub id: Option<String>,
pub retry: Duration,
pub elements: Option<String>,
pub selector: Option<String>,
pub mode: ElementPatchMode,
pub use_view_transition: bool,
pub view_transition_selector: Option<String>,
pub namespace: Namespace,
}
impl PatchElements {
pub fn new(elements: impl Into<String>) -> Self {
Self {
id: None,
retry: Duration::from_millis(consts::DEFAULT_SSE_RETRY_DURATION),
elements: Some(elements.into()),
selector: None,
mode: ElementPatchMode::default(),
use_view_transition: consts::DEFAULT_ELEMENTS_USE_VIEW_TRANSITIONS,
view_transition_selector: None,
namespace: Namespace::default(),
}
}
pub fn new_remove(selector: impl Into<String>) -> Self {
Self {
id: None,
retry: Duration::from_millis(consts::DEFAULT_SSE_RETRY_DURATION),
elements: None,
selector: Some(selector.into()),
mode: ElementPatchMode::Remove,
use_view_transition: consts::DEFAULT_ELEMENTS_USE_VIEW_TRANSITIONS,
view_transition_selector: None,
namespace: Namespace::default(),
}
}
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
}
pub fn selector(mut self, selector: impl Into<String>) -> Self {
self.selector = Some(selector.into());
self
}
pub fn mode(mut self, mode: ElementPatchMode) -> Self {
self.mode = mode;
self
}
pub fn use_view_transition(mut self, use_view_transition: bool) -> Self {
self.use_view_transition = use_view_transition;
self
}
pub fn view_transition_selector(mut self, selector: impl Into<String>) -> Self {
self.view_transition_selector = Some(selector.into());
self
}
pub fn namespace(mut self, namespace: Namespace) -> Self {
self.namespace = namespace;
self
}
#[inline]
pub fn into_datastar_event(mut self) -> DatastarEvent {
let id = self.id.take();
self.convert_to_datastar_event_inner(id)
}
#[inline]
pub fn as_datastar_event(&self) -> DatastarEvent {
self.convert_to_datastar_event_inner(self.id.clone())
}
fn convert_to_datastar_event_inner(&self, id: Option<String>) -> DatastarEvent {
let mut data: Vec<String> = Vec::new();
self.write_datalines(&mut data)
.expect("writing datalines to a Vec cannot fail");
DatastarEvent {
event: consts::EventType::PatchElements,
id,
retry: self.retry,
data,
}
}
pub(crate) fn write_datalines(&self, writer: &mut impl DatalineWriter) -> fmt::Result {
if let Some(selector) = &self.selector {
writer.write_dataline(format_args!(
"{} {}",
consts::SELECTOR_DATALINE_LITERAL,
selector
))?;
}
if self.mode != ElementPatchMode::default() {
writer.write_dataline(format_args!(
"{} {}",
consts::MODE_DATALINE_LITERAL,
self.mode.as_str()
))?;
}
if self.use_view_transition != consts::DEFAULT_ELEMENTS_USE_VIEW_TRANSITIONS {
writer.write_dataline(format_args!(
"{} {}",
consts::USE_VIEW_TRANSITION_DATALINE_LITERAL,
self.use_view_transition
))?;
if let Some(selector) = &self.view_transition_selector {
writer.write_dataline(format_args!(
"{} {}",
consts::VIEW_TRANSITION_SELECTOR_DATALINE_LITERAL,
selector
))?;
}
}
if self.namespace != Namespace::default() {
writer.write_dataline(format_args!(
"{} {}",
consts::NAMESPACE_DATALINE_LITERAL,
self.namespace.as_str()
))?;
}
if let Some(ref elements) = self.elements {
for line in elements.lines() {
writer.write_dataline(format_args!(
"{} {}",
consts::ELEMENTS_DATALINE_LITERAL,
line
))?;
}
}
Ok(())
}
}
impl From<&PatchElements> for DatastarEvent {
#[inline]
fn from(val: &PatchElements) -> Self {
val.as_datastar_event()
}
}
impl From<PatchElements> for DatastarEvent {
#[inline]
fn from(val: PatchElements) -> Self {
val.into_datastar_event()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serializes_current_patch_options() {
let event = PatchElements::new("<circle id=\"dot\" />")
.selector("#vis")
.mode(ElementPatchMode::Append)
.use_view_transition(true)
.view_transition_selector("#main")
.namespace(Namespace::Svg)
.into_datastar_event();
assert_eq!(
event.data,
[
"selector #vis",
"mode append",
"useViewTransition true",
"viewTransitionSelector #main",
"namespace svg",
"elements <circle id=\"dot\" />",
]
);
}
#[test]
fn omits_default_and_inactive_options() {
let event = PatchElements::new("<div id=\"message\">Hello</div>")
.view_transition_selector("#main")
.into_datastar_event();
assert_eq!(event.data, ["elements <div id=\"message\">Hello</div>"]);
}
#[test]
fn serializes_removal_and_conversions() {
let patch = PatchElements::new_remove("#message")
.id("elements-1")
.retry(Duration::from_millis(2_000));
for event in [
patch.as_datastar_event(),
DatastarEvent::from(&patch),
patch.clone().into_datastar_event(),
DatastarEvent::from(patch),
] {
assert_eq!(event.id.as_deref(), Some("elements-1"));
assert_eq!(event.retry, Duration::from_millis(2_000));
assert_eq!(event.data, ["selector #message", "mode remove"]);
}
}
}