factory.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. use crate::{innerlude::*, Attribute, Listener, VElement, VNode, VText};
  2. use bumpalo::{boxed::Box as BumpBox, Bump};
  3. use std::{
  4. cell::{Cell, RefCell},
  5. fmt::{Arguments, Debug},
  6. };
  7. /// This struct provides an ergonomic API to quickly build VNodes.
  8. ///
  9. /// NodeFactory is used to build VNodes in the component's memory space.
  10. /// This struct adds metadata to the final VNode about listeners, attributes, and children
  11. #[derive(Copy, Clone)]
  12. pub struct NodeFactory<'a> {
  13. pub(crate) scope: &'a ScopeState,
  14. pub(crate) bump: &'a Bump,
  15. }
  16. impl<'a> NodeFactory<'a> {
  17. /// Create a new [`NodeFactory`] from a [`Scope`] or [`ScopeState`]
  18. pub fn new(scope: &'a ScopeState) -> NodeFactory<'a> {
  19. NodeFactory {
  20. scope,
  21. bump: &scope.wip_frame().bump,
  22. }
  23. }
  24. /// Get the custom allocator for this component
  25. #[inline]
  26. pub fn bump(&self) -> &'a bumpalo::Bump {
  27. self.bump
  28. }
  29. /// Directly pass in text blocks without the need to use the format_args macro.
  30. pub fn static_text(&self, text: &'static str) -> VNode<'a> {
  31. VNode::Text(self.bump.alloc(VText {
  32. id: Default::default(),
  33. text,
  34. }))
  35. }
  36. /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
  37. ///
  38. /// Text that's static may be pointer compared, making it cheaper to diff
  39. pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
  40. match args.as_str() {
  41. Some(static_str) => (static_str, true),
  42. None => {
  43. use bumpalo::core_alloc::fmt::Write;
  44. let mut str_buf = bumpalo::collections::String::new_in(self.bump);
  45. str_buf.write_fmt(args).unwrap();
  46. (str_buf.into_bump_str(), false)
  47. }
  48. }
  49. }
  50. /// Create some text that's allocated along with the other vnodes
  51. ///
  52. pub fn text(&self, args: Arguments) -> VNode<'a> {
  53. let (text, _is_static) = self.raw_text(args);
  54. VNode::Text(self.bump.alloc(VText {
  55. text,
  56. id: Default::default(),
  57. }))
  58. }
  59. /// Create a new [`VNode::Element`] without the trait bound
  60. ///
  61. /// IE pass in "div" instead of `div`
  62. pub fn raw_element(
  63. &self,
  64. tag_name: &'static str,
  65. namespace: Option<&'static str>,
  66. listeners: &'a [Listener<'a>],
  67. attributes: &'a [Attribute<'a>],
  68. children: &'a [VNode<'a>],
  69. key: Option<Arguments>,
  70. ) -> VNode<'a> {
  71. let key = key.map(|f| self.raw_text(f).0);
  72. let mut items = self.scope.items.borrow_mut();
  73. for listener in listeners {
  74. let long_listener = unsafe { std::mem::transmute(listener) };
  75. items.listeners.push(long_listener);
  76. }
  77. VNode::Element(self.bump.alloc(VElement {
  78. tag: tag_name,
  79. key,
  80. namespace,
  81. listeners,
  82. attributes,
  83. children,
  84. id: Default::default(),
  85. parent: Default::default(),
  86. }))
  87. }
  88. /// Create a new [`Attribute`]
  89. pub fn attr(
  90. &self,
  91. name: &'static str,
  92. val: impl IntoAttributeValue<'a>,
  93. namespace: Option<&'static str>,
  94. is_volatile: bool,
  95. ) -> Attribute<'a> {
  96. Attribute {
  97. name,
  98. namespace,
  99. volatile: is_volatile,
  100. value: val.into_value(self.bump),
  101. }
  102. }
  103. /// Create a new [`Attribute`] using non-arguments
  104. pub fn custom_attr(
  105. &self,
  106. name: &'static str,
  107. value: AttributeValue<'a>,
  108. namespace: Option<&'static str>,
  109. is_volatile: bool,
  110. ) -> Attribute<'a> {
  111. Attribute {
  112. name,
  113. namespace,
  114. volatile: is_volatile,
  115. value,
  116. }
  117. }
  118. /// Create a new [`VNode::Component`]
  119. pub fn component<P>(
  120. &self,
  121. component: fn(Scope<'a, P>) -> Element,
  122. props: P,
  123. key: Option<Arguments>,
  124. fn_name: &'static str,
  125. ) -> VNode<'a>
  126. where
  127. P: Properties + 'a,
  128. {
  129. let vcomp = self.bump.alloc(VComponent {
  130. key: key.map(|f| self.raw_text(f).0),
  131. scope: Default::default(),
  132. can_memoize: P::IS_STATIC,
  133. user_fc: component as ComponentPtr,
  134. fn_name,
  135. props: RefCell::new(Some(Box::new(VComponentProps {
  136. props,
  137. memo: P::memoize, // smuggle the memoization function across borders
  138. // i'm sorry but I just need to bludgeon the lifetimes into place here
  139. // this is safe because we're managing all lifetimes to originate from previous calls
  140. // the intricacies of Rust's lifetime system make it difficult to properly express
  141. // the transformation from this specific lifetime to the for<'a> lifetime
  142. render_fn: unsafe { std::mem::transmute(component) },
  143. }))),
  144. });
  145. if !P::IS_STATIC {
  146. let vcomp = &*vcomp;
  147. let vcomp = unsafe { std::mem::transmute(vcomp) };
  148. self.scope.items.borrow_mut().borrowed_props.push(vcomp);
  149. }
  150. VNode::Component(vcomp)
  151. }
  152. /// Create a new [`Listener`]
  153. pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> {
  154. Listener {
  155. event,
  156. mounted_node: Cell::new(ElementId(0)),
  157. callback,
  158. }
  159. }
  160. /// Create a new [`VNode::Fragment`] from a root of the rsx! call
  161. pub fn fragment_root<'b, 'c>(
  162. self,
  163. node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
  164. ) -> VNode<'a> {
  165. let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
  166. for node in node_iter {
  167. nodes.push(node.into_vnode(self));
  168. }
  169. VNode::Fragment(self.bump.alloc(VFragment {
  170. children: nodes.into_bump_slice(),
  171. placeholder: Default::default(),
  172. key: None,
  173. }))
  174. }
  175. /// Create a new [`VNode::Fragment`] from any iterator
  176. pub fn fragment_from_iter<'c, I, J>(
  177. self,
  178. node_iter: impl IntoVNode<'a, I, J> + 'c,
  179. ) -> VNode<'a> {
  180. node_iter.into_vnode(self)
  181. }
  182. /// Create a new [`VNode`] from any iterator of children
  183. pub fn create_children(
  184. self,
  185. node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
  186. ) -> Element<'a> {
  187. let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
  188. for node in node_iter {
  189. nodes.push(node.into_vnode(self));
  190. }
  191. let children = nodes.into_bump_slice();
  192. Some(VNode::Fragment(self.bump.alloc(VFragment {
  193. children,
  194. key: None,
  195. placeholder: Default::default(),
  196. })))
  197. }
  198. /// Create a new [`EventHandler`] from an [`FnMut`]
  199. pub fn event_handler<T>(self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
  200. let handler: &mut dyn FnMut(T) = self.bump.alloc(f);
  201. let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
  202. let callback = RefCell::new(Some(caller));
  203. EventHandler { callback }
  204. }
  205. /// Create a refrence to a template
  206. pub fn template_ref(
  207. &self,
  208. template: Template<'static>,
  209. nodes: &'a [VNode<'a>],
  210. attributes: &'a [Attribute<'a>],
  211. listeners: &'a [Listener<'a>],
  212. key: Option<Arguments>,
  213. ) -> VNode<'a> {
  214. VNode::Template(
  215. self.bump.alloc(VTemplate {
  216. key: None,
  217. node_id: Cell::new(ElementId(0)),
  218. template,
  219. dynamic_nodes: self.bump.alloc([]),
  220. dynamic_attrs: self.bump.alloc([]),
  221. listeners,
  222. root_ids:
  223. bumpalo::vec![in self.bump; Cell::new(ElementId(0)); template.roots.len()]
  224. .into_bump_slice(),
  225. }),
  226. )
  227. }
  228. }
  229. impl Debug for NodeFactory<'_> {
  230. fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  231. Ok(())
  232. }
  233. }
  234. /// Trait implementations for use in the rsx! and html! macros.
  235. ///
  236. /// ## Details
  237. ///
  238. /// This section provides convenience methods and trait implementations for converting common structs into a format accepted
  239. /// by the macros.
  240. ///
  241. /// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
  242. /// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
  243. /// ```rust, ignore
  244. /// impl IntoIterator<Item = impl IntoVNode<'a>>
  245. /// ```
  246. ///
  247. /// As such, all node creation must go through the factory, which is only available in the component context.
  248. /// These strict requirements make it possible to manage lifetimes and state.
  249. pub trait IntoVNode<'a, I = (), J = ()> {
  250. /// Convert this into a [`VNode`], using the [`NodeFactory`] as a source of allocation
  251. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
  252. }
  253. // TODO: do we even need this? It almost seems better not to
  254. // // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  255. impl<'a> IntoVNode<'a> for VNode<'a> {
  256. fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
  257. self
  258. }
  259. }
  260. impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, '_> {
  261. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  262. self.call(cx)
  263. }
  264. }
  265. impl<'b> IntoVNode<'_> for &'b str {
  266. fn into_vnode(self, cx: NodeFactory) -> VNode {
  267. cx.text(format_args!("{}", self))
  268. }
  269. }
  270. impl IntoVNode<'_> for String {
  271. fn into_vnode(self, cx: NodeFactory) -> VNode {
  272. cx.text(format_args!("{}", self))
  273. }
  274. }
  275. impl IntoVNode<'_> for Arguments<'_> {
  276. fn into_vnode(self, cx: NodeFactory) -> VNode {
  277. cx.text(self)
  278. }
  279. }
  280. impl<'a> IntoVNode<'a> for &VNode<'a> {
  281. fn into_vnode(self, _cx: NodeFactory<'a>) -> VNode<'a> {
  282. // borrowed nodes are strange
  283. self.decouple()
  284. }
  285. }
  286. // Note that we're using the E as a generic but this is never crafted anyways.
  287. pub struct FromNodeIterator;
  288. impl<'a, T, I, E> IntoVNode<'a, FromNodeIterator, E> for T
  289. where
  290. T: IntoIterator<Item = I>,
  291. I: IntoVNode<'a, E>,
  292. {
  293. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  294. let mut nodes = bumpalo::collections::Vec::new_in(cx.bump);
  295. for node in self {
  296. nodes.push(node.into_vnode(cx));
  297. }
  298. let children = nodes.into_bump_slice();
  299. if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
  300. {
  301. // let bt = backtrace::Backtrace::new();
  302. let bt = "no backtrace available";
  303. // todo: make the backtrace prettier or remove it altogether
  304. log::error!(
  305. r#"
  306. Warning: Each child in an array or iterator should have a unique "key" prop.
  307. Not providing a key will lead to poor performance with lists.
  308. See docs.rs/dioxus for more information.
  309. -------------
  310. {:?}
  311. "#,
  312. bt
  313. );
  314. }
  315. VNode::Fragment(cx.bump.alloc(VFragment {
  316. children,
  317. placeholder: Default::default(),
  318. key: None,
  319. }))
  320. }
  321. }