nodes.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. //! Virtual Node Support
  2. //!
  3. //! VNodes represent lazily-constructed VDom trees that support diffing and event handlers. These VNodes should be *very*
  4. //! cheap and *very* fast to construct - building a full tree should be quick.
  5. use crate::innerlude::{
  6. empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, SuspendedContext,
  7. SyntheticEvent, FC,
  8. };
  9. use bumpalo::{boxed::Box as BumpBox, Bump};
  10. use std::{
  11. cell::{Cell, RefCell},
  12. fmt::{Arguments, Debug, Formatter},
  13. marker::PhantomData,
  14. };
  15. /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
  16. ///
  17. /// VNodes are designed to be lightweight and used with with a bump alloactor. To create a VNode, you can use either of:
  18. /// - the [`rsx`] macro
  19. /// - the [`html`] macro
  20. /// - the [`NodeFactory`] API
  21. pub enum VNode<'src> {
  22. /// Text VNodes simply bump-allocated (or static) string slices
  23. ///
  24. /// # Example
  25. ///
  26. /// ```
  27. /// let node = cx.render(rsx!{ "hello" }).unwrap();
  28. ///
  29. /// if let VNode::Text(vtext) = node {
  30. /// assert_eq!(vtext.text, "hello");
  31. /// assert_eq!(vtext.dom_id.get(), None);
  32. /// assert_eq!(vtext.is_static, true);
  33. /// }
  34. /// ```
  35. Text(VText<'src>),
  36. /// Element VNodes are VNodes that may contain attributes, listeners, a key, a tag, and children.
  37. ///
  38. /// # Example
  39. ///
  40. /// ```rust
  41. /// let node = cx.render(rsx!{
  42. /// div {
  43. /// key: "a",
  44. /// onclick: |e| log::info!("clicked"),
  45. /// hidden: "true",
  46. /// style: { background_color: "red" }
  47. /// "hello"
  48. /// }
  49. /// }).unwrap();
  50. /// if let VNode::Element(velement) = node {
  51. /// assert_eq!(velement.tag_name, "div");
  52. /// assert_eq!(velement.namespace, None);
  53. /// assert_eq!(velement.key, Some("a));
  54. /// }
  55. /// ```
  56. Element(&'src VElement<'src>),
  57. /// Fragment nodes may contain many VNodes without a single root.
  58. ///
  59. /// # Example
  60. ///
  61. /// ```rust
  62. /// rsx!{
  63. /// a {}
  64. /// link {}
  65. /// style {}
  66. /// "asd"
  67. /// Example {}
  68. /// }
  69. /// ```
  70. Fragment(VFragment<'src>),
  71. /// Component nodes represent a mounted component with props, children, and a key.
  72. ///
  73. /// # Example
  74. ///
  75. /// ```rust
  76. /// fn Example(cx: Context<()>) -> DomTree {
  77. /// todo!()
  78. /// }
  79. ///
  80. /// let node = cx.render(rsx!{
  81. /// Example {}
  82. /// }).unwrap();
  83. ///
  84. /// if let VNode::Component(vcomp) = node {
  85. /// assert_eq!(vcomp.user_fc, Example as *const ());
  86. /// }
  87. /// ```
  88. Component(&'src VComponent<'src>),
  89. /// Suspended VNodes represent chunks of the UI tree that are not yet ready to be displayed.
  90. ///
  91. /// These nodes currently can only be constructed via the [`use_suspense`] hook.
  92. ///
  93. /// # Example
  94. ///
  95. /// ```rust
  96. /// rsx!{
  97. /// }
  98. /// ```
  99. Suspended(&'src VSuspended<'src>),
  100. /// Anchors are a type of placeholder VNode used when fragments don't contain any children.
  101. ///
  102. /// Anchors cannot be directly constructed via public APIs.
  103. ///
  104. /// # Example
  105. ///
  106. /// ```rust
  107. /// let node = cx.render(rsx! ( Fragment {} )).unwrap();
  108. /// if let VNode::Fragment(frag) = node {
  109. /// let root = &frag.children[0];
  110. /// assert_eq!(root, VNode::Anchor);
  111. /// }
  112. /// ```
  113. Anchor(VAnchor),
  114. }
  115. impl<'src> VNode<'src> {
  116. /// Get the VNode's "key" used in the keyed diffing algorithm.
  117. pub fn key(&self) -> Option<&'src str> {
  118. match &self {
  119. VNode::Element(el) => el.key,
  120. VNode::Component(c) => c.key,
  121. VNode::Fragment(f) => f.key,
  122. VNode::Text(_t) => None,
  123. VNode::Suspended(_s) => None,
  124. VNode::Anchor(_f) => None,
  125. }
  126. }
  127. /// Get the ElementID of the mounted VNode.
  128. ///
  129. /// Panics if the mounted ID is None or if the VNode is not represented by a single Element.
  130. pub fn mounted_id(&self) -> ElementId {
  131. self.try_mounted_id().unwrap()
  132. }
  133. /// Try to get the ElementID of the mounted VNode.
  134. ///
  135. /// Returns None if the VNode is not mounted, or if the VNode cannot be presented by a mounted ID (Fragment/Component)
  136. pub fn try_mounted_id(&self) -> Option<ElementId> {
  137. match &self {
  138. VNode::Text(el) => el.dom_id.get(),
  139. VNode::Element(el) => el.dom_id.get(),
  140. VNode::Anchor(el) => el.dom_id.get(),
  141. VNode::Suspended(el) => el.dom_id.get(),
  142. VNode::Fragment(_) => None,
  143. VNode::Component(_) => None,
  144. }
  145. }
  146. }
  147. /// A placeholder node only generated when Fragments don't have any children.
  148. pub struct VAnchor {
  149. pub dom_id: Cell<Option<ElementId>>,
  150. }
  151. /// A bump-alloacted string slice and metadata.
  152. pub struct VText<'src> {
  153. pub text: &'src str,
  154. pub dom_id: Cell<Option<ElementId>>,
  155. pub is_static: bool,
  156. }
  157. /// A list of VNodes with no single root.
  158. pub struct VFragment<'src> {
  159. pub key: Option<&'src str>,
  160. pub children: &'src [VNode<'src>],
  161. pub is_static: bool,
  162. }
  163. /// An element like a "div" with children, listeners, and attributes.
  164. pub struct VElement<'a> {
  165. pub tag_name: &'static str,
  166. pub namespace: Option<&'static str>,
  167. pub key: Option<&'a str>,
  168. pub dom_id: Cell<Option<ElementId>>,
  169. pub parent_id: Cell<Option<ElementId>>,
  170. pub listeners: &'a [Listener<'a>],
  171. pub attributes: &'a [Attribute<'a>],
  172. pub children: &'a [VNode<'a>],
  173. }
  174. /// A trait for any generic Dioxus Element.
  175. ///
  176. /// This trait provides the ability to use custom elements in the `rsx!` macro.
  177. ///
  178. /// ```rust
  179. /// struct my_element;
  180. ///
  181. /// impl DioxusElement for my_element {
  182. /// const TAG_NAME: "my_element";
  183. /// const NAME_SPACE: None;
  184. /// }
  185. ///
  186. /// let _ = rsx!{
  187. /// my_element {}
  188. /// };
  189. /// ```
  190. pub trait DioxusElement {
  191. const TAG_NAME: &'static str;
  192. const NAME_SPACE: Option<&'static str>;
  193. #[inline]
  194. fn tag_name(&self) -> &'static str {
  195. Self::TAG_NAME
  196. }
  197. #[inline]
  198. fn namespace(&self) -> Option<&'static str> {
  199. Self::NAME_SPACE
  200. }
  201. }
  202. /// An attribute on a DOM node, such as `id="my-thing"` or
  203. /// `href="https://example.com"`.
  204. #[derive(Clone, Debug)]
  205. pub struct Attribute<'a> {
  206. pub name: &'static str,
  207. pub value: &'a str,
  208. pub is_static: bool,
  209. pub is_volatile: bool,
  210. // Doesn't exist in the html spec.
  211. // Used in Dioxus to denote "style" tags.
  212. pub namespace: Option<&'static str>,
  213. }
  214. /// An event listener.
  215. /// IE onclick, onkeydown, etc
  216. pub struct Listener<'bump> {
  217. /// The ID of the node that this listener is mounted to
  218. /// Used to generate the event listener's ID on the DOM
  219. pub mounted_node: Cell<Option<ElementId>>,
  220. /// The type of event to listen for.
  221. ///
  222. /// IE "click" - whatever the renderer needs to attach the listener by name.
  223. pub event: &'static str,
  224. /// The actual callback that the user specified
  225. pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(SyntheticEvent) + 'bump>>>,
  226. }
  227. /// Virtual Components for custom user-defined components
  228. /// Only supports the functional syntax
  229. pub struct VComponent<'src> {
  230. pub key: Option<&'src str>,
  231. pub associated_scope: Cell<Option<ScopeId>>,
  232. pub is_static: bool,
  233. // Function pointer to the FC that was used to generate this component
  234. pub user_fc: *const (),
  235. pub(crate) caller: &'src dyn for<'b> Fn(&'b Scope) -> DomTree<'b>,
  236. pub(crate) children: &'src [VNode<'src>],
  237. pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
  238. pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
  239. pub(crate) can_memoize: bool,
  240. // Raw pointer into the bump arena for the props of the component
  241. pub(crate) raw_props: *const (),
  242. }
  243. pub struct VSuspended<'a> {
  244. pub task_id: u64,
  245. pub dom_id: Cell<Option<ElementId>>,
  246. pub callback: RefCell<Option<BumpBox<'a, dyn FnMut(SuspendedContext<'a>) -> DomTree<'a>>>>,
  247. }
  248. /// This struct provides an ergonomic API to quickly build VNodes.
  249. ///
  250. /// NodeFactory is used to build VNodes in the component's memory space.
  251. /// This struct adds metadata to the final VNode about listeners, attributes, and children
  252. #[derive(Copy, Clone)]
  253. pub struct NodeFactory<'a> {
  254. pub(crate) bump: &'a Bump,
  255. }
  256. impl<'a> NodeFactory<'a> {
  257. pub fn new(bump: &'a Bump) -> NodeFactory<'a> {
  258. NodeFactory { bump }
  259. }
  260. #[inline]
  261. pub fn bump(&self) -> &'a bumpalo::Bump {
  262. &self.bump
  263. }
  264. pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a, F>) -> DomTree<'a>
  265. where
  266. F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  267. {
  268. Some(lazy_nodes.into_vnode(NodeFactory { bump: self.bump }))
  269. }
  270. pub fn unstable_place_holder() -> VNode<'static> {
  271. VNode::Text(VText {
  272. text: "",
  273. dom_id: empty_cell(),
  274. is_static: true,
  275. })
  276. }
  277. /// Directly pass in text blocks without the need to use the format_args macro.
  278. pub fn static_text(&self, text: &'static str) -> VNode<'a> {
  279. VNode::Text(VText {
  280. dom_id: empty_cell(),
  281. text,
  282. is_static: true,
  283. })
  284. }
  285. /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
  286. ///
  287. /// Text that's static may be pointer compared, making it cheaper to diff
  288. pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
  289. match args.as_str() {
  290. Some(static_str) => (static_str, true),
  291. None => {
  292. use bumpalo::core_alloc::fmt::Write;
  293. let mut str_buf = bumpalo::collections::String::new_in(self.bump());
  294. str_buf.write_fmt(args).unwrap();
  295. (str_buf.into_bump_str(), false)
  296. }
  297. }
  298. }
  299. /// Create some text that's allocated along with the other vnodes
  300. ///
  301. pub fn text(&self, args: Arguments) -> VNode<'a> {
  302. let (text, is_static) = self.raw_text(args);
  303. VNode::Text(VText {
  304. text,
  305. is_static,
  306. dom_id: empty_cell(),
  307. })
  308. }
  309. pub fn element<L, A, V>(
  310. &self,
  311. el: impl DioxusElement,
  312. listeners: L,
  313. attributes: A,
  314. children: V,
  315. key: Option<Arguments>,
  316. ) -> VNode<'a>
  317. where
  318. L: 'a + AsRef<[Listener<'a>]>,
  319. A: 'a + AsRef<[Attribute<'a>]>,
  320. V: 'a + AsRef<[VNode<'a>]>,
  321. {
  322. self.raw_element(
  323. el.tag_name(),
  324. el.namespace(),
  325. listeners,
  326. attributes,
  327. children,
  328. key,
  329. )
  330. }
  331. pub fn raw_element<L, A, V>(
  332. &self,
  333. tag_name: &'static str,
  334. namespace: Option<&'static str>,
  335. listeners: L,
  336. attributes: A,
  337. children: V,
  338. key: Option<Arguments>,
  339. ) -> VNode<'a>
  340. where
  341. L: 'a + AsRef<[Listener<'a>]>,
  342. A: 'a + AsRef<[Attribute<'a>]>,
  343. V: 'a + AsRef<[VNode<'a>]>,
  344. {
  345. let listeners: &'a L = self.bump().alloc(listeners);
  346. let listeners = listeners.as_ref();
  347. let attributes: &'a A = self.bump().alloc(attributes);
  348. let attributes = attributes.as_ref();
  349. let children: &'a V = self.bump().alloc(children);
  350. let children = children.as_ref();
  351. let key = key.map(|f| self.raw_text(f).0);
  352. VNode::Element(self.bump().alloc(VElement {
  353. tag_name,
  354. key,
  355. namespace,
  356. listeners,
  357. attributes,
  358. children,
  359. dom_id: empty_cell(),
  360. parent_id: empty_cell(),
  361. }))
  362. }
  363. pub fn attr(
  364. &self,
  365. name: &'static str,
  366. val: Arguments,
  367. namespace: Option<&'static str>,
  368. is_volatile: bool,
  369. ) -> Attribute<'a> {
  370. let (value, is_static) = self.raw_text(val);
  371. Attribute {
  372. name,
  373. value,
  374. is_static,
  375. namespace,
  376. is_volatile,
  377. }
  378. }
  379. pub fn component<P, V>(
  380. &self,
  381. component: FC<P>,
  382. props: P,
  383. key: Option<Arguments>,
  384. children: V,
  385. ) -> VNode<'a>
  386. where
  387. P: Properties + 'a,
  388. V: 'a + AsRef<[VNode<'a>]>,
  389. {
  390. let bump = self.bump();
  391. let children: &'a V = bump.alloc(children);
  392. let children = children.as_ref();
  393. let props = bump.alloc(props);
  394. let raw_props = props as *mut P as *mut ();
  395. let user_fc = component as *const ();
  396. let comparator: Option<&dyn Fn(&VComponent) -> bool> = Some(bump.alloc_with(|| {
  397. move |other: &VComponent| {
  398. if user_fc == other.user_fc {
  399. // Safety
  400. // - We guarantee that FC<P> is the same by function pointer
  401. // - Because FC<P> is the same, then P must be the same (even with generics)
  402. // - Non-static P are autoderived to memoize as false
  403. // - This comparator is only called on a corresponding set of bumpframes
  404. let props_memoized = unsafe {
  405. let real_other: &P = &*(other.raw_props as *const _ as *const P);
  406. props.memoize(&real_other)
  407. };
  408. // It's only okay to memoize if there are no children and the props can be memoized
  409. // Implementing memoize is unsafe and done automatically with the props trait
  410. match (props_memoized, children.len() == 0) {
  411. (true, true) => true,
  412. _ => false,
  413. }
  414. } else {
  415. false
  416. }
  417. }
  418. }));
  419. let drop_props = {
  420. // create a closure to drop the props
  421. let mut has_dropped = false;
  422. let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
  423. move || unsafe {
  424. if !has_dropped {
  425. let real_other = raw_props as *mut _ as *mut P;
  426. let b = BumpBox::from_raw(real_other);
  427. std::mem::drop(b);
  428. has_dropped = true;
  429. } else {
  430. panic!("Drop props called twice - this is an internal failure of Dioxus");
  431. }
  432. }
  433. });
  434. let drop_props = unsafe { BumpBox::from_raw(drop_props) };
  435. RefCell::new(Some(drop_props))
  436. };
  437. let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
  438. let key = key.map(|f| self.raw_text(f).0);
  439. let caller: &'a mut dyn for<'b> Fn(&'b Scope) -> DomTree<'b> =
  440. bump.alloc(move |scope: &Scope| -> DomTree {
  441. let props: &'_ P = unsafe { &*(raw_props as *const P) };
  442. let res = component(Context { scope }, props);
  443. unsafe { std::mem::transmute(res) }
  444. });
  445. let can_memoize = children.len() == 0 && P::IS_STATIC;
  446. VNode::Component(bump.alloc(VComponent {
  447. user_fc,
  448. comparator,
  449. raw_props,
  450. children,
  451. caller,
  452. is_static,
  453. key,
  454. can_memoize,
  455. drop_props,
  456. associated_scope: Cell::new(None),
  457. }))
  458. }
  459. pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
  460. let children = node_iter.into_vnode_list(self);
  461. // TODO
  462. // We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
  463. //
  464. // if cfg!(debug_assertions) {
  465. // if children.len() > 1 {
  466. // if children.last().unwrap().key().is_none() {
  467. // log::error!(
  468. // r#"
  469. // Warning: Each child in an array or iterator should have a unique "key" prop.
  470. // Not providing a key will lead to poor performance with lists.
  471. // See docs.rs/dioxus for more information.
  472. // ---
  473. // To help you identify where this error is coming from, we've generated a backtrace.
  474. // "#,
  475. // );
  476. // }
  477. // }
  478. // }
  479. VNode::Fragment(VFragment {
  480. children,
  481. key: None,
  482. is_static: false,
  483. })
  484. }
  485. }
  486. /// Trait implementations for use in the rsx! and html! macros.
  487. ///
  488. /// ## Details
  489. ///
  490. /// This section provides convenience methods and trait implementations for converting common structs into a format accepted
  491. /// by the macros.
  492. ///
  493. /// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
  494. /// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
  495. /// ```
  496. /// impl IntoIterator<Item = impl IntoVNode<'a>>
  497. /// ```
  498. ///
  499. /// As such, all node creation must go through the factory, which is only availble in the component context.
  500. /// These strict requirements make it possible to manage lifetimes and state.
  501. pub trait IntoVNode<'a> {
  502. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
  503. }
  504. pub trait IntoVNodeList<'a> {
  505. fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>];
  506. }
  507. impl<'a, T, V> IntoVNodeList<'a> for T
  508. where
  509. T: IntoIterator<Item = V>,
  510. V: IntoVNode<'a>,
  511. {
  512. fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>] {
  513. let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
  514. for node in self.into_iter() {
  515. nodes.push(node.into_vnode(cx));
  516. }
  517. if nodes.len() == 0 {
  518. nodes.push(VNode::Anchor(VAnchor {
  519. dom_id: empty_cell(),
  520. }));
  521. }
  522. nodes.into_bump_slice()
  523. }
  524. }
  525. /// Child nodes of the parent component.
  526. ///
  527. /// # Example
  528. ///
  529. /// ```rust
  530. /// let children = cx.children();
  531. /// let first_node = &children[0];
  532. /// rsx!{
  533. /// h1 { {first_node} }
  534. /// p { {&children[1..]} }
  535. /// }
  536. /// ```
  537. ///
  538. pub struct ScopeChildren<'a>(pub &'a [VNode<'a>]);
  539. impl Copy for ScopeChildren<'_> {}
  540. impl<'a> Clone for ScopeChildren<'a> {
  541. fn clone(&self) -> Self {
  542. ScopeChildren(self.0)
  543. }
  544. }
  545. impl ScopeChildren<'_> {
  546. // dangerous method - used to fix the associated lifetime
  547. pub(crate) unsafe fn extend_lifetime(self) -> ScopeChildren<'static> {
  548. std::mem::transmute(self)
  549. }
  550. // dangerous method - used to fix the associated lifetime
  551. pub(crate) unsafe fn shorten_lifetime<'a>(self) -> ScopeChildren<'a> {
  552. std::mem::transmute(self)
  553. }
  554. }
  555. impl<'a> IntoVNodeList<'a> for ScopeChildren<'a> {
  556. fn into_vnode_list(self, _: NodeFactory<'a>) -> &'a [VNode<'a>] {
  557. self.0
  558. }
  559. }
  560. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  561. impl<'a> IntoIterator for VNode<'a> {
  562. type Item = VNode<'a>;
  563. type IntoIter = std::iter::Once<Self::Item>;
  564. fn into_iter(self) -> Self::IntoIter {
  565. std::iter::once(self)
  566. }
  567. }
  568. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  569. impl<'a> IntoVNode<'a> for VNode<'a> {
  570. fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
  571. self
  572. }
  573. }
  574. /// A concrete type provider for closures that build VNode structures.
  575. ///
  576. /// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
  577. /// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
  578. ///
  579. ///
  580. /// ```rust
  581. /// LazyNodes::new(|f| f.element("div", [], [], [] None))
  582. /// ```
  583. pub struct LazyNodes<'a, G>
  584. where
  585. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  586. {
  587. inner: G,
  588. _p: PhantomData<&'a ()>,
  589. }
  590. impl<'a, G> LazyNodes<'a, G>
  591. where
  592. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  593. {
  594. pub fn new(f: G) -> Self {
  595. Self {
  596. inner: f,
  597. _p: PhantomData {},
  598. }
  599. }
  600. }
  601. // Our blanket impl
  602. impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
  603. where
  604. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  605. {
  606. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  607. (self.inner)(cx)
  608. }
  609. }
  610. // Our blanket impl
  611. impl<'a, G> IntoIterator for LazyNodes<'a, G>
  612. where
  613. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  614. {
  615. type Item = Self;
  616. type IntoIter = std::iter::Once<Self::Item>;
  617. fn into_iter(self) -> Self::IntoIter {
  618. std::iter::once(self)
  619. }
  620. }
  621. // Conveniently, we also support "null" (nothing) passed in
  622. impl IntoVNode<'_> for () {
  623. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  624. cx.fragment_from_iter(None as Option<VNode>)
  625. }
  626. }
  627. // Conveniently, we also support "None"
  628. impl IntoVNode<'_> for Option<()> {
  629. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  630. cx.fragment_from_iter(None as Option<VNode>)
  631. }
  632. }
  633. impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
  634. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  635. match self {
  636. Some(n) => n,
  637. None => cx.fragment_from_iter(None as Option<VNode>),
  638. }
  639. }
  640. }
  641. impl IntoVNode<'_> for &'static str {
  642. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  643. cx.static_text(self)
  644. }
  645. }
  646. impl IntoVNode<'_> for Arguments<'_> {
  647. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  648. cx.text(self)
  649. }
  650. }
  651. impl Debug for NodeFactory<'_> {
  652. fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  653. Ok(())
  654. }
  655. }
  656. impl Debug for VNode<'_> {
  657. fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
  658. match &self {
  659. VNode::Element(el) => s
  660. .debug_struct("VElement")
  661. .field("name", &el.tag_name)
  662. .field("key", &el.key)
  663. .finish(),
  664. VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
  665. VNode::Anchor(_) => write!(s, "VAnchor"),
  666. VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
  667. VNode::Suspended { .. } => write!(s, "VSuspended"),
  668. VNode::Component(comp) => write!(
  669. s,
  670. "VComponent {{ fc: {:?}, children: {:?} }}",
  671. comp.user_fc, comp.children
  672. ),
  673. }
  674. }
  675. }