use std::borrow::Cow;
use serde::Serialize;
use topcoat_core::context::Cx;
use topcoat_view::{AttributeValueViewParts, PartsWriter};
#[derive(Debug, Clone)]
pub struct Js {
parts: Vec<JsPart>,
}
#[derive(Debug, Clone)]
enum JsPart {
Source(Cow<'static, str>),
Raw(&'static str),
Surrogate(String),
}
impl Js {
#[must_use]
pub fn source(js: impl Into<Cow<'static, str>>) -> Self {
Self {
parts: vec![JsPart::Source(js.into())],
}
}
#[must_use]
pub fn builder() -> JsBuilder {
JsBuilder { parts: Vec::new() }
}
pub(crate) fn write(&self, parts: &mut PartsWriter<'_>) {
for part in &self.parts {
match part {
JsPart::Source(Cow::Borrowed(js)) => {
parts.push_static_str(js);
}
JsPart::Source(Cow::Owned(js)) => {
parts.push_str(js);
}
JsPart::Raw(js) => {
parts.push_static_str_unescaped(js);
}
JsPart::Surrogate(json) => {
parts
.push_promoted_str_unescaped(&"cx.hydrate(")
.push_str(json)
.push_promoted_str_unescaped(&")");
}
}
}
}
}
impl AttributeValueViewParts for Js {
#[inline]
fn attribute_present(&self) -> bool {
true
}
#[inline]
fn into_view_parts(self, _cx: &Cx, parts: &mut PartsWriter<'_>) {
self.write(parts);
}
}
#[derive(Debug)]
pub struct JsBuilder {
parts: Vec<JsPart>,
}
impl JsBuilder {
#[must_use]
pub fn source(mut self, js: impl Into<Cow<'static, str>>) -> Self {
self.parts.push(JsPart::Source(js.into()));
self
}
#[must_use]
pub fn raw(mut self, js: &'static str) -> Self {
self.parts.push(JsPart::Raw(js));
self
}
#[must_use]
pub fn surrogate(mut self, value: &(impl Serialize + ?Sized)) -> Self {
let json = serde_json::to_string(value).expect("failed to serialize surrogate value");
self.parts.push(JsPart::Surrogate(json));
self
}
#[must_use]
pub fn build(self) -> Js {
Js { parts: self.parts }
}
}