use serde::Serialize;
use crate::{Hydrate, escape_into, json};
pub struct Html<'p, P, F = fn(&mut String, &P)> {
pub(crate) props: Given<'p, P>,
pub(crate) render: F,
pub(crate) island: Option<&'static str>,
pub(crate) hydrate: Option<Hydrate>,
}
impl<'p, P: Serialize, F: Fn(&mut String, &P)> Html<'p, P, F> {
#[doc(hidden)]
#[must_use]
pub fn markup(props: &'p P, render: F) -> Self {
Html {
props: Given::Borrowed(props),
render,
island: None,
hydrate: None,
}
}
#[doc(hidden)]
#[must_use]
pub fn island(name: &'static str, props: &'p P, render: F) -> Self {
Html {
props: Given::Borrowed(props),
render,
island: Some(name),
hydrate: None,
}
}
#[doc(hidden)]
#[must_use]
pub fn markup_owned(props: P, render: F) -> Self {
Html {
props: Given::Owned(props),
render,
island: None,
hydrate: None,
}
}
#[doc(hidden)]
#[must_use]
pub fn island_owned(name: &'static str, props: P, render: F) -> Self {
Html {
props: Given::Owned(props),
render,
island: Some(name),
hydrate: None,
}
}
#[must_use]
pub fn hydrate(mut self, when: Hydrate) -> Self {
self.hydrate = Some(when);
self
}
pub fn render_to(&self, buf: &mut String) {
let props = self.props.get();
match self.island {
None => (self.render)(buf, props),
Some(name) => island_into(buf, name, self.hydrate.as_ref(), props, &self.render),
}
}
#[must_use]
pub fn into_string(self) -> String {
let mut out = String::new();
self.render_to(&mut out);
out
}
}
pub(crate) enum Given<'p, P> {
Borrowed(&'p P),
Owned(P),
}
impl<P> Given<'_, P> {
#[inline]
pub(crate) fn get(&self) -> &P {
match self {
Given::Borrowed(props) => props,
Given::Owned(props) => props,
}
}
}
impl<P: std::fmt::Debug, F> std::fmt::Debug for Html<'_, P, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_struct("Html");
debug.field("island", &self.island);
if let Some(hydrate) = &self.hydrate {
debug.field("hydrate", hydrate);
}
debug
.field("props", self.props.get())
.finish_non_exhaustive()
}
}
#[cfg(feature = "maud")]
#[cfg_attr(docsrs, doc(cfg(feature = "maud")))]
impl<P: Serialize, F: Fn(&mut String, &P)> maud::Render for Html<'_, P, F> {
fn render_to(&self, buf: &mut String) {
Html::render_to(self, buf);
}
}
fn island_into<P: Serialize>(
out: &mut String,
name: &str,
hydrate: Option<&Hydrate>,
props: &P,
render: &impl Fn(&mut String, &P),
) {
out.push_str("<div data-island=\"");
escape_into(out, name);
out.push_str("\" data-props=\"");
json::escaped_into(out, props);
if let Some(hydrate) = hydrate {
out.push_str("\" data-hydrate=\"");
escape_into(out, &hydrate.attribute());
}
out.push_str("\">");
render(out, props);
out.push_str("</div>");
}
#[cfg(test)]
mod tests;