use futures_channel::mpsc::Sender;
use std::{
fmt::{Display, Write},
sync::{Arc, RwLock},
};
#[derive(Clone, Debug, Default)]
struct MountPath {
parent: Option<Arc<MountPath>>,
id: usize,
}
impl MountPath {
fn child(&self) -> Self {
Self {
parent: Some(Arc::new(self.clone())),
id: 0,
}
}
}
impl Display for MountPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(parent) = &self.parent {
write!(f, "{},", parent)?;
}
write!(f, "{}", self.id)
}
}
pub(crate) struct StreamingRenderer<E = std::convert::Infallible> {
channel: RwLock<Sender<Result<String, E>>>,
current_path: RwLock<MountPath>,
}
impl<E> StreamingRenderer<E> {
pub(crate) fn new(
before_body: impl Display,
mut render_into: Sender<Result<String, E>>,
) -> Self {
let start_html = before_body.to_string();
_ = render_into.start_send(Ok(start_html));
Self {
channel: render_into.into(),
current_path: Default::default(),
}
}
pub(crate) fn render(&self, html: impl Display) {
_ = self
.channel
.write()
.unwrap()
.start_send(Ok(html.to_string()));
}
pub(crate) fn render_placeholder<W: Write + ?Sized>(
&self,
html: impl FnOnce(&mut W) -> std::fmt::Result,
into: &mut W,
) -> Result<Mount, std::fmt::Error> {
let id = self.current_path.read().unwrap().clone();
self.current_path.write().unwrap().id += 1;
let old_path = std::mem::replace(&mut *self.current_path.write().unwrap(), id.child());
html(into)?;
*self.current_path.write().unwrap() = old_path;
Ok(Mount { id })
}
pub(crate) fn replace_placeholder<W: Write + ?Sized>(
&self,
id: Mount,
html: impl FnOnce(&mut W) -> std::fmt::Result,
data: impl Display,
into: &mut W,
) -> std::fmt::Result {
write!(into, r#"<div id="ds-{id}-r" hidden>"#)?;
let old_path = std::mem::replace(&mut *self.current_path.write().unwrap(), id.id.child());
html(into)?;
*self.current_path.write().unwrap() = old_path;
write!(
into,
r#"</div><script>window.dx_hydrate([{id}], "{data}")</script>"#
)
}
pub(crate) fn close_with_error(&self, error: E) {
_ = self.channel.write().unwrap().start_send(Err(error));
}
}
#[derive(Clone, Debug)]
pub(crate) struct Mount {
id: MountPath,
}
impl Display for Mount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.id)
}
}