dioxus/packages/core/src/nodes.rs

738 lines
23 KiB
Rust
Raw Normal View History

2022-12-03 00:24:49 +00:00
use crate::{
any_props::AnyProps, arena::ElementId, Element, Event, LazyNodes, ScopeId, ScopeState,
};
2022-11-29 21:31:04 +00:00
use bumpalo::boxed::Box as BumpBox;
2022-12-03 00:24:49 +00:00
use bumpalo::Bump;
2022-11-03 00:29:18 +00:00
use std::{
2022-11-04 00:34:42 +00:00
any::{Any, TypeId},
2022-11-03 00:29:18 +00:00
cell::{Cell, RefCell},
2022-12-03 00:24:49 +00:00
fmt::Arguments,
future::Future,
2022-12-26 14:37:50 +00:00
ops::Deref,
2022-11-03 00:29:18 +00:00
};
pub type TemplateId = &'static str;
2022-12-03 00:24:49 +00:00
/// The actual state of the component's most recent computation
///
/// Because Dioxus accepts components in the form of `async fn(Scope) -> Result<VNode>`, we need to support both
/// sync and async versions.
///
/// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
/// you might need to handle the case where there's no node immediately ready.
pub enum RenderReturn<'a> {
/// A currently-available element
Sync(Element<'a>),
/// An ongoing future that will resolve to a [`Element`]
Async(BumpBox<'a, dyn Future<Output = Element<'a>> + 'a>),
}
/// A reference to a template along with any context needed to hydrate it
2022-11-20 01:07:29 +00:00
///
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
/// static parts of the template.
2022-11-23 05:32:26 +00:00
#[derive(Debug, Clone)]
2022-11-02 01:42:29 +00:00
pub struct VNode<'a> {
2022-11-20 01:07:29 +00:00
/// The key given to the root of this template.
///
/// In fragments, this is the key of the first child. In other cases, it is the key of the root.
2022-11-03 08:24:20 +00:00
pub key: Option<&'a str>,
2022-11-20 01:07:29 +00:00
/// When rendered, this template will be linked to its parent manually
2022-11-18 04:00:39 +00:00
pub parent: Option<ElementId>,
2022-11-02 01:42:29 +00:00
2022-11-20 01:07:29 +00:00
/// The static nodes and static descriptor of the template
2022-11-02 08:00:37 +00:00
pub template: Template<'static>,
2022-11-20 01:07:29 +00:00
/// The IDs for the roots of this template - to be used when moving the template around and removing it from
/// the actual Dom
2022-12-13 02:31:30 +00:00
pub root_ids: &'a [Cell<Option<ElementId>>],
2022-11-20 01:07:29 +00:00
/// The dynamic parts of the template
2022-11-03 00:29:18 +00:00
pub dynamic_nodes: &'a [DynamicNode<'a>],
2022-11-20 01:07:29 +00:00
/// The dynamic parts of the template
2022-11-03 00:29:18 +00:00
pub dynamic_attrs: &'a [Attribute<'a>],
}
2022-11-06 22:28:41 +00:00
impl<'a> VNode<'a> {
2022-12-03 00:24:49 +00:00
/// Create a template with no nodes that will be skipped over during diffing
2022-11-23 05:32:26 +00:00
pub fn empty() -> Element<'a> {
Some(VNode {
2022-11-23 05:32:26 +00:00
key: None,
parent: None,
root_ids: &[],
dynamic_nodes: &[],
dynamic_attrs: &[],
template: Template {
2022-12-03 00:24:49 +00:00
name: "dioxus-empty",
2022-11-23 05:32:26 +00:00
roots: &[],
node_paths: &[],
attr_paths: &[],
},
})
}
2022-11-25 07:12:29 +00:00
2022-12-03 00:24:49 +00:00
/// Load a dynamic root at the given index
///
/// Returns [`None`] if the root is actually a static node (Element/Text)
2022-11-25 07:12:29 +00:00
pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode<'a>> {
match &self.template.roots[idx] {
TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
2022-11-25 07:12:29 +00:00
Some(&self.dynamic_nodes[*id])
}
}
}
2022-12-05 21:03:52 +00:00
pub(crate) fn clear_listeners(&self) {
for attr in self.dynamic_attrs {
if let AttributeValue::Listener(l) = &attr.value {
2022-12-09 22:18:37 +00:00
l.0.borrow_mut().take();
2022-12-05 21:03:52 +00:00
}
}
}
2022-11-06 22:28:41 +00:00
}
2022-12-03 00:24:49 +00:00
/// A static layout of a UI tree that describes a set of dynamic and static nodes.
///
/// This is the core innovation in Dioxus. Most UIs are made of static nodes, yet participate in diffing like any
/// dynamic node. This struct can be created at compile time. It promises that its name is unique, allow Dioxus to use
/// its static description of the UI to skip immediately to the dynamic nodes during diffing.
///
/// For this to work properly, the [`Template::name`] *must* be unique across your entire project. This can be done via variety of
/// ways, with the suggested approach being the unique code location (file, line, col, etc).
2022-12-01 05:46:15 +00:00
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
2022-12-03 00:24:49 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
2022-11-02 08:00:37 +00:00
pub struct Template<'a> {
2022-12-03 00:24:49 +00:00
/// The name of the template. This must be unique across your entire program for template diffing to work properly
///
/// If two templates have the same name, it's likely that Dioxus will panic when diffing.
pub name: &'a str,
/// The list of template nodes that make up the template
///
/// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
2022-11-02 08:00:37 +00:00
pub roots: &'a [TemplateNode<'a>],
2022-12-03 00:24:49 +00:00
/// The paths of each node relative to the root of the template.
///
/// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
/// topmost element, not the `roots` field.
2022-11-03 08:24:20 +00:00
pub node_paths: &'a [&'a [u8]],
2022-12-03 00:24:49 +00:00
/// The paths of each dynamic attribute relative to the root of the template
///
/// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
/// topmost element, not the `roots` field.
2022-11-03 08:24:20 +00:00
pub attr_paths: &'a [&'a [u8]],
2022-11-02 08:00:37 +00:00
}
2022-12-13 02:31:30 +00:00
impl<'a> Template<'a> {
/// Is this template worth caching at all, since it's completely runtime?
///
/// There's no point in saving templates that are completely dynamic, since they'll be recreated every time anyway.
pub fn is_completely_dynamic(&self) -> bool {
use TemplateNode::*;
self.roots
.iter()
.all(|root| matches!(root, Dynamic { .. } | DynamicText { .. }))
}
}
2022-12-03 00:24:49 +00:00
/// A statically known node in a layout.
///
/// This can be created at compile time, saving the VirtualDom time when diffing the tree
#[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize), serde(tag = "type"))]
pub enum TemplateNode<'a> {
2022-12-03 00:24:49 +00:00
/// An statically known element in the dom.
///
/// In HTML this would be something like `<div id="123"> </div>`
Element {
2022-12-03 00:24:49 +00:00
/// The name of the element
///
/// IE for a div, it would be the string "div"
tag: &'a str,
2022-12-03 00:24:49 +00:00
/// The namespace of the element
///
/// In HTML, this would be a valid URI that defines a namespace for all elements below it
/// SVG is an example of this namespace
namespace: Option<&'a str>,
2022-12-03 00:24:49 +00:00
/// A list of possibly dynamic attribues for this element
///
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
attrs: &'a [TemplateAttribute<'a>],
2022-12-03 00:24:49 +00:00
/// A list of template nodes that define another set of template nodes
children: &'a [TemplateNode<'a>],
},
2022-12-03 00:24:49 +00:00
/// This template node is just a piece of static text
2022-12-07 00:37:28 +00:00
Text {
/// The actual text
text: &'a str,
},
2022-12-03 00:24:49 +00:00
/// This template node is unknown, and needs to be created at runtime.
2022-12-07 00:37:28 +00:00
Dynamic {
/// The index of the dynamic node in the VNode's dynamic_nodes list
id: usize,
},
2022-12-03 00:24:49 +00:00
/// This template node is known to be some text, but needs to be created at runtime
///
/// This is separate from the pure Dynamic variant for various optimizations
2022-12-07 00:37:28 +00:00
DynamicText {
/// The index of the dynamic node in the VNode's dynamic_nodes list
id: usize,
},
}
2022-12-03 00:24:49 +00:00
/// A node created at runtime
///
/// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
2022-11-16 07:22:41 +00:00
#[derive(Debug)]
2022-11-03 08:24:20 +00:00
pub enum DynamicNode<'a> {
2022-12-03 00:24:49 +00:00
/// A component node
///
/// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
/// assigned scope are dynamic.
///
/// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
/// the render function at runtime
2022-11-20 01:07:29 +00:00
Component(VComponent<'a>),
2022-12-03 00:24:49 +00:00
/// A text node
2022-11-20 01:07:29 +00:00
Text(VText<'a>),
2022-12-03 00:24:49 +00:00
/// A placeholder
///
/// Used by suspense when a node isn't ready and by fragments that don't render anything
///
/// In code, this is just an ElementId whose initial value is set to 0 upon creation
2022-12-13 02:31:30 +00:00
Placeholder(VPlaceholder),
2022-12-03 00:24:49 +00:00
/// A list of VNodes.
///
/// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
/// or iterators.
Fragment(&'a [VNode<'a>]),
}
2022-12-03 00:24:49 +00:00
impl Default for DynamicNode<'_> {
fn default() -> Self {
2022-12-01 04:54:30 +00:00
Self::Placeholder(Default::default())
}
2022-11-20 01:07:29 +00:00
}
2022-12-03 00:24:49 +00:00
/// An instance of a child component
2022-11-20 01:07:29 +00:00
pub struct VComponent<'a> {
2022-12-03 00:24:49 +00:00
/// The name of this component
2022-11-20 01:07:29 +00:00
pub name: &'static str,
2022-12-03 00:24:49 +00:00
/// Are the props valid for the 'static lifetime?
///
/// Internally, this is used as a guarantee. Externally, this might be incorrect, so don't count on it.
///
/// This flag is assumed by the [`crate::Properties`] trait which is unsafe to implement
2022-11-20 01:07:29 +00:00
pub static_props: bool,
2022-12-03 00:24:49 +00:00
/// The assigned Scope for this component
2022-11-20 01:07:29 +00:00
pub scope: Cell<Option<ScopeId>>,
2022-12-03 00:24:49 +00:00
/// The function pointer of the component, known at compile time
///
/// It is possible that components get folded at comppile time, so these shouldn't be really used as a key
2022-11-20 23:58:05 +00:00
pub render_fn: *const (),
2022-12-03 00:24:49 +00:00
2022-12-13 02:31:30 +00:00
pub(crate) props: RefCell<Option<Box<dyn AnyProps<'a> + 'a>>>,
2022-11-20 23:58:05 +00:00
}
impl<'a> std::fmt::Debug for VComponent<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VComponent")
.field("name", &self.name)
.field("static_props", &self.static_props)
.field("scope", &self.scope)
.finish()
}
2022-11-20 01:07:29 +00:00
}
2022-12-03 00:24:49 +00:00
/// An instance of some text, mounted to the DOM
2022-11-20 01:07:29 +00:00
#[derive(Debug)]
pub struct VText<'a> {
2022-12-03 00:24:49 +00:00
/// The actual text itself
2022-11-20 01:07:29 +00:00
pub value: &'a str,
2022-12-03 00:24:49 +00:00
/// The ID of this node in the real DOM
2022-12-13 02:31:30 +00:00
pub id: Cell<Option<ElementId>>,
}
/// A placeholder node, used by suspense and fragments
#[derive(Debug, Default)]
pub struct VPlaceholder {
/// The ID of this node in the real DOM
pub id: Cell<Option<ElementId>>,
2022-11-20 01:07:29 +00:00
}
2022-12-03 00:24:49 +00:00
/// An attribute of the TemplateNode, created at compile time
#[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
2022-11-02 08:00:37 +00:00
pub enum TemplateAttribute<'a> {
2022-12-03 00:24:49 +00:00
/// This attribute is entirely known at compile time, enabling
2022-11-02 08:00:37 +00:00
Static {
2022-12-03 00:24:49 +00:00
/// The name of this attribute.
///
/// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
2022-12-01 05:46:15 +00:00
name: &'a str,
2022-12-03 00:24:49 +00:00
/// The value of this attribute, known at compile time
///
/// Currently this only accepts &str, so values, even if they're known at compile time, are not known
2022-11-02 08:00:37 +00:00
value: &'a str,
2022-12-03 00:24:49 +00:00
/// The namespace of this attribute. Does not exist in the HTML spec
2022-12-01 05:46:15 +00:00
namespace: Option<&'a str>,
2022-11-02 08:00:37 +00:00
},
2022-12-03 00:24:49 +00:00
/// The attribute in this position is actually determined dynamically at runtime
///
/// This is the index into the dynamic_attributes field on the container VNode
Dynamic {
/// The index
id: usize,
},
}
2022-12-03 00:24:49 +00:00
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
2022-11-16 07:22:41 +00:00
#[derive(Debug)]
pub struct Attribute<'a> {
2022-12-03 00:24:49 +00:00
/// The name of the attribute.
2022-11-03 00:29:18 +00:00
pub name: &'a str,
2022-12-03 00:24:49 +00:00
/// The value of the attribute
2022-11-03 00:29:18 +00:00
pub value: AttributeValue<'a>,
2022-12-03 00:24:49 +00:00
/// The namespace of the attribute.
///
/// Doesnt exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
pub namespace: Option<&'static str>,
2022-12-03 00:24:49 +00:00
/// The element in the DOM that this attribute belongs to
2022-11-03 00:29:18 +00:00
pub mounted_element: Cell<ElementId>,
2022-12-03 00:24:49 +00:00
/// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
2022-11-03 08:24:20 +00:00
pub volatile: bool,
}
2022-12-03 00:24:49 +00:00
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
///
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
/// variant.
2022-12-09 22:18:37 +00:00
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serialize", serde(untagged))]
#[derive(Clone)]
pub enum AttributeValue<'a> {
2022-12-03 00:24:49 +00:00
/// Text attribute
Text(&'a str),
2022-12-03 00:24:49 +00:00
/// A float
Float(f64),
/// Signed integer
Int(i64),
/// Boolean
Bool(bool),
2022-12-03 00:24:49 +00:00
/// A listener, like "onclick"
2022-12-09 22:18:37 +00:00
Listener(ListenerCb<'a>),
2022-12-03 00:24:49 +00:00
/// An arbitrary value that implements PartialEq and is static
2022-12-11 23:31:15 +00:00
Any(AnyValueContainer),
2022-12-03 00:24:49 +00:00
/// A "none" value, resulting in the removal of an attribute from the dom
2022-11-03 00:29:18 +00:00
None,
}
2022-12-09 22:18:37 +00:00
pub type ListenerCbInner<'a> = RefCell<Option<BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>>>;
pub struct ListenerCb<'a>(pub ListenerCbInner<'a>);
impl Clone for ListenerCb<'_> {
fn clone(&self) -> Self {
panic!("ListenerCb cannot be cloned")
}
}
#[cfg(feature = "serialize")]
impl<'a> serde::Serialize for ListenerCb<'a> {
fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
panic!("ListenerCb cannot be serialized")
}
}
#[cfg(feature = "serialize")]
impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> {
fn deserialize<D>(_: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
panic!("ListenerCb cannot be deserialized")
}
}
/// A boxed value that implements PartialEq and Any
#[derive(Clone)]
2022-12-11 23:31:15 +00:00
#[cfg(not(feature = "sync_attributes"))]
pub struct AnyValueContainer(pub std::rc::Rc<dyn AnyValue>);
2022-12-10 18:50:15 +00:00
2022-12-11 23:31:15 +00:00
#[derive(Clone)]
#[cfg(feature = "sync_attributes")]
/// A boxed value that implements PartialEq, Any, Sync, and Send
2022-12-11 23:31:15 +00:00
pub struct AnyValueContainer(pub std::sync::Arc<dyn AnyValue>);
impl PartialEq for AnyValueContainer {
2022-12-10 18:50:15 +00:00
fn eq(&self, other: &Self) -> bool {
self.0.any_cmp(other.0.as_ref())
}
}
2022-12-11 23:31:15 +00:00
impl AnyValueContainer {
/// Create a new AnyValueContainer containing the specified data.
pub fn new<T: AnyValue + 'static>(value: T) -> Self {
2022-12-11 23:31:15 +00:00
#[cfg(feature = "sync_attributes")]
return Self(std::sync::Arc::new(value));
#[cfg(not(feature = "sync_attributes"))]
return Self(std::rc::Rc::new(value));
2022-12-11 23:31:15 +00:00
}
2022-12-10 18:50:15 +00:00
/// Returns a reference to the inner value.
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
2022-12-26 14:37:50 +00:00
self.0.deref().as_any().downcast_ref()
2022-12-10 18:50:15 +00:00
}
2022-12-26 14:37:50 +00:00
/// Checks if the type of the inner value is 'T'.
pub fn is<T: Any>(&self) -> bool {
2022-12-26 14:37:50 +00:00
self.0.deref().as_any().is::<T>()
2022-12-10 18:50:15 +00:00
}
}
#[test]
fn test_any_value_rc() {
2022-12-11 23:31:15 +00:00
let a = AnyValueContainer::new(1i32);
2022-12-10 18:50:15 +00:00
assert_eq!(a.downcast_ref::<i32>(), Some(&1i32));
assert_eq!(a.downcast_ref::<i64>(), None);
2022-12-11 23:31:15 +00:00
assert!(a.is::<i32>());
assert!(!a.is::<i64>());
2022-12-10 18:50:15 +00:00
}
2022-12-09 22:18:37 +00:00
#[cfg(feature = "serialize")]
2022-12-11 23:31:15 +00:00
impl serde::Serialize for AnyValueContainer {
2022-12-09 22:18:37 +00:00
fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
panic!("AnyValueBox cannot be serialized")
}
}
#[cfg(feature = "serialize")]
2022-12-11 23:31:15 +00:00
impl<'de> serde::Deserialize<'de> for AnyValueContainer {
2022-12-09 22:18:37 +00:00
fn deserialize<D>(_: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
panic!("AnyValueBox cannot be deserialized")
}
}
2022-11-29 21:46:25 +00:00
2022-11-04 00:34:42 +00:00
impl<'a> std::fmt::Debug for AttributeValue<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
2022-11-08 06:55:22 +00:00
Self::Listener(_) => f.debug_tuple("Listener").finish(),
Self::Any(_) => f.debug_tuple("Any").finish(),
2022-11-04 00:34:42 +00:00
Self::None => write!(f, "None"),
}
}
}
impl<'a> PartialEq for AttributeValue<'a> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
(Self::Float(l0), Self::Float(r0)) => l0 == r0,
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
2022-11-08 06:55:22 +00:00
(Self::Listener(_), Self::Listener(_)) => true,
2022-12-10 18:50:15 +00:00
(Self::Any(l0), Self::Any(r0)) => l0 == r0,
2022-12-09 22:18:37 +00:00
_ => false,
2022-11-04 00:34:42 +00:00
}
}
}
// Some renderers need attributes to be sync and send. The rest of the attributes are already sync and send, but there is no way to force Any values to be sync and send on the renderer side.
// The sync_attributes flag restricts any valuse to be sync and send.
2022-12-03 00:24:49 +00:00
#[doc(hidden)]
2022-12-11 23:31:15 +00:00
#[cfg(feature = "sync_attributes")]
2022-12-26 14:37:50 +00:00
pub trait AnyValue: Sync + Send + 'static {
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
2022-12-26 14:37:50 +00:00
fn as_any(&self) -> &dyn Any;
fn type_id(&self) -> TypeId {
self.as_any().type_id()
}
}
#[cfg(feature = "sync_attributes")]
2022-12-26 14:37:50 +00:00
impl<T: Any + PartialEq + Send + Sync + 'static> AnyValue for T {
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
2022-12-26 14:37:50 +00:00
if self.type_id() != other.type_id() {
return false;
}
self == unsafe { &*(other as *const _ as *const T) }
}
2022-12-26 14:37:50 +00:00
fn as_any(&self) -> &dyn Any {
self
}
}
#[doc(hidden)]
#[cfg(not(feature = "sync_attributes"))]
2022-12-26 14:37:50 +00:00
pub trait AnyValue: 'static {
2022-11-04 00:34:42 +00:00
fn any_cmp(&self, other: &dyn AnyValue) -> bool;
2022-12-26 14:37:50 +00:00
fn as_any(&self) -> &dyn Any;
fn type_id(&self) -> TypeId {
self.as_any().type_id()
}
}
2022-11-08 06:55:22 +00:00
#[cfg(not(feature = "sync_attributes"))]
2022-12-26 14:37:50 +00:00
impl<T: Any + PartialEq + 'static> AnyValue for T {
2022-11-04 00:34:42 +00:00
fn any_cmp(&self, other: &dyn AnyValue) -> bool {
2022-12-26 14:37:50 +00:00
if self.type_id() != other.type_id() {
return false;
}
self == unsafe { &*(other as *const _ as *const T) }
}
2022-12-11 23:31:15 +00:00
2022-12-26 14:37:50 +00:00
fn as_any(&self) -> &dyn Any {
self
2022-12-11 23:31:15 +00:00
}
}
2022-12-03 00:24:49 +00:00
#[doc(hidden)]
pub trait ComponentReturn<'a, A = ()> {
fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
}
impl<'a> ComponentReturn<'a> for Element<'a> {
fn into_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
RenderReturn::Sync(self)
}
}
#[doc(hidden)]
pub struct AsyncMarker;
impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
where
F: Future<Output = Element<'a>> + 'a,
{
fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a> {
let f: &mut dyn Future<Output = Element<'a>> = cx.bump().alloc(self);
RenderReturn::Async(unsafe { BumpBox::from_raw(f) })
}
}
impl<'a> RenderReturn<'a> {
pub(crate) unsafe fn extend_lifetime_ref<'c>(&self) -> &'c RenderReturn<'c> {
unsafe { std::mem::transmute(self) }
}
pub(crate) unsafe fn extend_lifetime<'c>(self) -> RenderReturn<'c> {
unsafe { std::mem::transmute(self) }
}
}
/// A trait that allows various items to be converted into a dynamic node for the rsx macro
pub trait IntoDynNode<'a, A = ()> {
/// Consume this item along with a scopestate and produce a DynamicNode
///
/// You can use the bump alloactor of the scopestate to creat the dynamic node
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a>;
}
impl<'a> IntoDynNode<'a> for () {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
DynamicNode::default()
}
}
impl<'a> IntoDynNode<'a> for VNode<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
DynamicNode::Fragment(_cx.bump().alloc([self]))
}
}
2022-12-05 22:16:54 +00:00
impl<'a> IntoDynNode<'a> for DynamicNode<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
self
}
}
2022-12-03 00:24:49 +00:00
impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
match self {
Some(val) => val.into_vnode(_cx),
None => DynamicNode::default(),
}
}
}
impl<'a> IntoDynNode<'a> for &Element<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
match self.as_ref() {
Some(val) => val.clone().into_vnode(_cx),
2022-12-03 00:24:49 +00:00
_ => DynamicNode::default(),
}
}
}
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
DynamicNode::Fragment(cx.bump().alloc([self.call(cx)]))
}
}
impl<'a> IntoDynNode<'_> for &'a str {
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
cx.text_node(format_args!("{}", self))
}
}
impl IntoDynNode<'_> for String {
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
cx.text_node(format_args!("{}", self))
}
}
impl<'b> IntoDynNode<'b> for Arguments<'_> {
fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> {
cx.text_node(self)
}
}
impl<'a> IntoDynNode<'a> for &'a VNode<'a> {
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
DynamicNode::Fragment(_cx.bump().alloc([VNode {
parent: self.parent,
template: self.template,
root_ids: self.root_ids,
key: self.key,
dynamic_nodes: self.dynamic_nodes,
dynamic_attrs: self.dynamic_attrs,
}]))
}
}
pub trait IntoTemplate<'a> {
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a>;
}
impl<'a> IntoTemplate<'a> for VNode<'a> {
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> {
self
}
}
impl<'a> IntoTemplate<'a> for Element<'a> {
fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> {
match self {
Some(val) => val.into_template(_cx),
_ => VNode::empty().unwrap(),
}
}
}
2022-12-03 00:24:49 +00:00
impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> {
fn into_template(self, cx: &'a ScopeState) -> VNode<'a> {
self.call(cx)
}
}
// Note that we're using the E as a generic but this is never crafted anyways.
#[doc(hidden)]
pub struct FromNodeIterator;
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
where
T: Iterator<Item = I>,
I: IntoTemplate<'a>,
{
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
nodes.extend(self.into_iter().map(|node| node.into_template(cx)));
match nodes.into_bump_slice() {
children if children.is_empty() => DynamicNode::default(),
children => DynamicNode::Fragment(children),
}
}
}
/// A value that can be converted into an attribute value
pub trait IntoAttributeValue<'a> {
/// Convert into an attribute value
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a>;
}
impl<'a> IntoAttributeValue<'a> for &'a str {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Text(self)
}
}
2022-12-09 22:18:37 +00:00
2022-12-03 00:24:49 +00:00
impl<'a> IntoAttributeValue<'a> for f64 {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Float(self)
}
}
2022-12-09 22:18:37 +00:00
2022-12-03 00:24:49 +00:00
impl<'a> IntoAttributeValue<'a> for i64 {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Int(self)
}
}
2022-12-09 22:18:37 +00:00
2022-12-03 00:24:49 +00:00
impl<'a> IntoAttributeValue<'a> for bool {
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Bool(self)
}
}
2022-12-09 22:18:37 +00:00
2022-12-03 00:24:49 +00:00
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
use bumpalo::core_alloc::fmt::Write;
let mut str_buf = bumpalo::collections::String::new_in(bump);
str_buf.write_fmt(self).unwrap();
AttributeValue::Text(str_buf.into_bump_str())
}
}
2022-12-09 22:18:37 +00:00
2022-12-11 23:31:15 +00:00
impl<'a> IntoAttributeValue<'a> for AnyValueContainer {
2022-12-09 22:18:37 +00:00
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
AttributeValue::Any(self)
}
}