nodes.rs 22 KB

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