use crate::{
renderer::{CastFrom, Rndr},
view::{Position, PositionState},
};
#[cfg(any(debug_assertions, leptos_debuginfo))]
use std::cell::Cell;
use std::{cell::RefCell, panic::Location, rc::Rc};
use web_sys::{Comment, Element, Node, Text};
#[cfg(feature = "mark_branches")]
const COMMENT_NODE: u16 = 8;
#[derive(Debug)]
pub struct Cursor(Rc<RefCell<crate::renderer::types::Node>>);
impl Clone for Cursor {
fn clone(&self) -> Self {
Self(Rc::clone(&self.0))
}
}
impl Cursor
where
crate::renderer::types::Element: AsRef<crate::renderer::types::Node>,
{
pub fn new(root: crate::renderer::types::Element) -> Self {
let root = <crate::renderer::types::Element as AsRef<
crate::renderer::types::Node,
>>::as_ref(&root)
.clone();
Self(Rc::new(RefCell::new(root)))
}
pub fn current(&self) -> crate::renderer::types::Node {
self.0.borrow().clone()
}
pub fn child(&self) {
let mut inner = self.0.borrow_mut();
if let Some(node) = Rndr::first_child(&inner) {
*inner = node;
}
#[cfg(feature = "mark_branches")]
{
while inner.node_type() == COMMENT_NODE {
if let Some(content) = inner.text_content() {
if content.starts_with("bo") || content.starts_with("bc") {
if let Some(sibling) = Rndr::next_sibling(&inner) {
*inner = sibling;
continue;
}
}
}
break;
}
}
}
pub fn sibling(&self) {
let mut inner = self.0.borrow_mut();
if let Some(node) = Rndr::next_sibling(&inner) {
*inner = node;
}
#[cfg(feature = "mark_branches")]
{
while inner.node_type() == COMMENT_NODE {
if let Some(content) = inner.text_content() {
if content.starts_with("bo") || content.starts_with("bc") {
if let Some(sibling) = Rndr::next_sibling(&inner) {
*inner = sibling;
continue;
}
}
}
break;
}
}
}
pub fn parent(&self) {
let mut inner = self.0.borrow_mut();
if let Some(node) = Rndr::get_parent(&inner) {
*inner = node;
}
}
pub fn set(&self, node: crate::renderer::types::Node) {
*self.0.borrow_mut() = node;
}
pub fn next_placeholder(
&self,
position: &PositionState,
) -> crate::renderer::types::Placeholder {
self.advance_to_placeholder(position);
let marker = self.current();
crate::renderer::types::Placeholder::cast_from(marker.clone())
.unwrap_or_else(|| failed_to_cast_marker_node(marker))
}
pub fn advance_to_placeholder(&self, position: &PositionState) {
if position.get() == Position::FirstChild {
self.child();
} else {
self.sibling();
}
position.set(Position::NextChild);
}
}
#[cfg(any(debug_assertions, leptos_debuginfo))]
thread_local! {
static CURRENTLY_HYDRATING: Cell<Option<&'static Location<'static>>> = const { Cell::new(None) };
}
pub(crate) fn set_currently_hydrating(
location: Option<&'static Location<'static>>,
) {
#[cfg(any(debug_assertions, leptos_debuginfo))]
{
CURRENTLY_HYDRATING.set(location);
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
{
_ = location;
}
}
pub(crate) fn failed_to_cast_element(tag_name: &str, node: Node) -> Element {
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
{
_ = node;
unreachable!();
}
#[cfg(any(debug_assertions, leptos_debuginfo))]
{
let hydrating = CURRENTLY_HYDRATING
.take()
.map(|n| n.to_string())
.unwrap_or_else(|| "{unknown}".to_string());
web_sys::console::error_3(
&wasm_bindgen::JsValue::from_str(&format!(
"A hydration error occurred while trying to hydrate an \
element defined at {hydrating}.\n\nThe framework expected an \
HTML <{tag_name}> element, but found this instead: ",
)),
&node,
&wasm_bindgen::JsValue::from_str(
"\n\nThe hydration mismatch may have occurred slightly \
earlier, but this is the first time the framework found a \
node of an unexpected type.",
),
);
panic!(
"Unrecoverable hydration error. Please read the error message \
directly above this for more details."
);
}
}
pub(crate) fn failed_to_cast_marker_node(node: Node) -> Comment {
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
{
_ = node;
unreachable!();
}
#[cfg(any(debug_assertions, leptos_debuginfo))]
{
let hydrating = CURRENTLY_HYDRATING
.take()
.map(|n| n.to_string())
.unwrap_or_else(|| "{unknown}".to_string());
web_sys::console::error_3(
&wasm_bindgen::JsValue::from_str(&format!(
"A hydration error occurred while trying to hydrate an \
element defined at {hydrating}.\n\nThe framework expected a \
marker node, but found this instead: ",
)),
&node,
&wasm_bindgen::JsValue::from_str(
"\n\nThe hydration mismatch may have occurred slightly \
earlier, but this is the first time the framework found a \
node of an unexpected type.",
),
);
panic!(
"Unrecoverable hydration error. Please read the error message \
directly above this for more details."
);
}
}
pub(crate) fn failed_to_cast_text_node(node: Node) -> Text {
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
{
_ = node;
unreachable!();
}
#[cfg(any(debug_assertions, leptos_debuginfo))]
{
let hydrating = CURRENTLY_HYDRATING
.take()
.map(|n| n.to_string())
.unwrap_or_else(|| "{unknown}".to_string());
web_sys::console::error_3(
&wasm_bindgen::JsValue::from_str(&format!(
"A hydration error occurred while trying to hydrate an \
element defined at {hydrating}.\n\nThe framework expected a \
text node, but found this instead: ",
)),
&node,
&wasm_bindgen::JsValue::from_str(
"\n\nThe hydration mismatch may have occurred slightly \
earlier, but this is the first time the framework found a \
node of an unexpected type.",
),
);
panic!(
"Unrecoverable hydration error. Please read the error message \
directly above this for more details."
);
}
}