use std::{
rc::Rc,
task::{Context, Poll},
};
use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
mod bindings;
#[allow(unused)]
pub use bindings::*;
mod eval;
pub use eval::*;
pub mod head;
pub use head::{Meta, MetaProps, Script, ScriptProps, Style, StyleProps, Title, TitleProps};
fn format_attributes(attributes: &[(&str, String)]) -> String {
let mut formatted = String::from("[");
for (key, value) in attributes {
formatted.push_str(&format!("[{key:?}, {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
.map(|c| format!("\"{c}\""))
.unwrap_or("null".to_string());
format!(r#"{helpers};window.createElementInHead("{tag}", {attributes}, {children});"#)
}
pub trait Document {
fn new_evaluator(&self, js: String) -> GenerationalBox<Box<dyn Evaluator>>;
fn set_title(&self, title: String) {
self.new_evaluator(format!("document.title = {title:?};"));
}
fn create_meta(&self, props: MetaProps) {
let attributes = props.attributes();
let js = create_element_in_head("meta", &attributes, None);
self.new_evaluator(js);
}
fn create_script(&self, props: ScriptProps) {
let attributes = props.attributes();
let js = create_element_in_head("script", &attributes, props.script_contents());
self.new_evaluator(js);
}
fn create_style(&self, props: StyleProps) {
let attributes = props.attributes();
let js = create_element_in_head("style", &attributes, props.style_contents());
self.new_evaluator(js);
}
fn create_link(&self, props: head::LinkProps) {
let attributes = props.attributes();
let js = create_element_in_head("link", &attributes, None);
self.new_evaluator(js);
}
fn as_any(&self) -> &dyn std::any::Any;
}
pub struct NoOpDocument;
impl Document for NoOpDocument {
fn new_evaluator(&self, _js: String) -> GenerationalBox<Box<dyn Evaluator>> {
tracing::error!("Eval is not supported on this platform. If you are using dioxus fullstack, you can wrap your code with `client! {{}}` to only include the code that runs eval in the client bundle.");
UnsyncStorage::owner().insert(Box::new(NoOpEvaluator))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
struct NoOpEvaluator;
impl Evaluator for NoOpEvaluator {
fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> {
Err(EvalError::Unsupported)
}
fn poll_recv(
&mut self,
_context: &mut Context<'_>,
) -> Poll<Result<serde_json::Value, EvalError>> {
Poll::Ready(Err(EvalError::Unsupported))
}
fn poll_join(
&mut self,
_context: &mut Context<'_>,
) -> Poll<Result<serde_json::Value, EvalError>> {
Poll::Ready(Err(EvalError::Unsupported))
}
}
pub fn document() -> Rc<dyn Document> {
dioxus_core::prelude::try_consume_context::<Rc<dyn Document>>()
.unwrap_or_else(|| Rc::new(NoOpDocument) as Rc<dyn Document>)
}