dioxus_document/
document.rsuse std::sync::Arc;
use super::*;
pub type DocumentContext = Arc<dyn Document>;
fn format_string_for_js(s: &str) -> String {
    let escaped = s
        .replace('\\', "\\\\")
        .replace('\n', "\\n")
        .replace('\r', "\\r")
        .replace('"', "\\\"");
    format!("\"{escaped}\"")
}
fn format_attributes(attributes: &[(&str, String)]) -> String {
    let mut formatted = String::from("[");
    for (key, value) in attributes {
        formatted.push_str(&format!(
            "[{}, {}],",
            format_string_for_js(key),
            format_string_for_js(value)
        ));
    }
    if formatted.ends_with(',') {
        formatted.pop();
    }
    formatted.push(']');
    formatted
}
fn create_element_in_head(
    tag: &str,
    attributes: &[(&str, String)],
    children: Option<String>,
) -> String {
    let helpers = include_str!("./js/head.js");
    let attributes = format_attributes(attributes);
    let children = children
        .as_deref()
        .map(format_string_for_js)
        .unwrap_or("null".to_string());
    let tag = format_string_for_js(tag);
    format!(r#"{helpers};window.createElementInHead({tag}, {attributes}, {children});"#)
}
pub trait Document: 'static {
    fn eval(&self, js: String) -> Eval;
    fn set_title(&self, title: String) {
        self.eval(format!("document.title = {title:?};"));
    }
    fn create_head_element(
        &self,
        name: &str,
        attributes: &[(&str, String)],
        contents: Option<String>,
    ) {
        self.eval(create_element_in_head(name, attributes, contents));
    }
    fn create_meta(&self, props: MetaProps) {
        let attributes = props.attributes();
        self.create_head_element("meta", &attributes, None);
    }
    fn create_script(&self, props: ScriptProps) {
        let attributes = props.attributes();
        match (&props.src, props.script_contents()) {
            (_, Ok(contents)) => self.create_head_element("script", &attributes, Some(contents)),
            (Some(_), _) => self.create_head_element("script", &attributes, None),
            (None, Err(err)) => err.log("Script"),
        }
    }
    fn create_style(&self, props: StyleProps) {
        let mut attributes = props.attributes();
        match (&props.href, props.style_contents()) {
            (_, Ok(contents)) => self.create_head_element("style", &attributes, Some(contents)),
            (Some(_), _) => {
                attributes.push(("type", "text/css".into()));
                self.create_head_element("link", &attributes, None)
            }
            (None, Err(err)) => err.log("Style"),
        };
    }
    fn create_link(&self, props: LinkProps) {
        let attributes = props.attributes();
        self.create_head_element("link", &attributes, None);
    }
}
#[derive(Default)]
pub struct NoOpDocument;
impl Document for NoOpDocument {
    fn eval(&self, _: String) -> Eval {
        let owner = generational_box::Owner::default();
        struct NoOpEvaluator;
        impl Evaluator for NoOpEvaluator {
            fn poll_join(
                &mut self,
                _: &mut std::task::Context<'_>,
            ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
                std::task::Poll::Ready(Err(EvalError::Unsupported))
            }
            fn poll_recv(
                &mut self,
                _: &mut std::task::Context<'_>,
            ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
                std::task::Poll::Ready(Err(EvalError::Unsupported))
            }
            fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> {
                Err(EvalError::Unsupported)
            }
        }
        Eval::new(owner.insert(Box::new(NoOpEvaluator)))
    }
}