rsx_call.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. //! The actual rsx! macro implementation.
  2. //!
  3. //! This mostly just defers to the root TemplateBody with some additional tooling to provide better errors.
  4. //! Currently the additional tooling doesn't do much.
  5. use proc_macro2::TokenStream as TokenStream2;
  6. use quote::ToTokens;
  7. use std::{cell::Cell, fmt::Debug};
  8. use syn::{
  9. parse::{Parse, ParseStream},
  10. Result,
  11. };
  12. use crate::{BodyNode, TemplateBody};
  13. /// The Callbody is the contents of the rsx! macro
  14. ///
  15. /// It is a list of BodyNodes, which are the different parts of the template.
  16. /// The Callbody contains no information about how the template will be rendered, only information about the parsed tokens.
  17. ///
  18. /// Every callbody should be valid, so you can use it to build a template.
  19. /// To generate the code used to render the template, use the ToTokens impl on the Callbody, or with the `render_with_location` method.
  20. ///
  21. /// Ideally we don't need the metadata here and can bake the idx-es into the templates themselves but I haven't figured out how to do that yet.
  22. #[derive(Debug, Clone)]
  23. pub struct CallBody {
  24. pub body: TemplateBody,
  25. pub template_idx: Cell<usize>,
  26. }
  27. impl Parse for CallBody {
  28. fn parse(input: ParseStream) -> Result<Self> {
  29. // Defer to the `new` method such that we can wire up hotreload information
  30. Ok(CallBody::new(input.parse()?))
  31. }
  32. }
  33. impl ToTokens for CallBody {
  34. fn to_tokens(&self, out: &mut TokenStream2) {
  35. self.body.to_tokens(out)
  36. }
  37. }
  38. impl CallBody {
  39. /// Create a new CallBody from a TemplateBody
  40. ///
  41. /// This will overwrite all internal metadata regarding hotreloading.
  42. pub fn new(body: TemplateBody) -> Self {
  43. let body = CallBody {
  44. body,
  45. template_idx: Cell::new(0),
  46. };
  47. body.body.template_idx.set(body.next_template_idx());
  48. body.cascade_hotreload_info(&body.body.roots);
  49. body
  50. }
  51. /// Parse a stream into a CallBody. Return all error immediately instead of trying to partially expand the macro
  52. ///
  53. /// This should be preferred over `parse` if you are outside of a macro
  54. pub fn parse_strict(input: ParseStream) -> Result<Self> {
  55. // todo: actually throw warnings if there are any
  56. Self::parse(input)
  57. }
  58. /// With the entire knowledge of the macro call, wire up location information for anything hotreloading
  59. /// specific. It's a little bit simpler just to have a global id per callbody than to try and track it
  60. /// relative to each template, though we could do that if we wanted to.
  61. ///
  62. /// For now this is just information for ifmts and templates so that when they generate, they can be
  63. /// tracked back to the original location in the source code, to support formatted string hotreloading.
  64. ///
  65. /// Note that there are some more complex cases we could in theory support, but have bigger plans
  66. /// to enable just pure rust hotreloading that would make those tricks moot. So, manage more of
  67. /// the simple cases until that proper stuff ships.
  68. ///
  69. /// We need to make sure to wire up:
  70. /// - subtemplate IDs
  71. /// - ifmt IDs
  72. /// - dynamic node IDs
  73. /// - dynamic attribute IDs
  74. /// - paths for dynamic nodes and attributes
  75. ///
  76. /// Lots of wiring!
  77. ///
  78. /// However, here, we only need to wire up template IDs since TemplateBody will handle the rest.
  79. ///
  80. /// This is better though since we can save the relevant data on the structures themselves.
  81. fn cascade_hotreload_info(&self, nodes: &[BodyNode]) {
  82. for node in nodes.iter() {
  83. match node {
  84. BodyNode::Element(el) => {
  85. self.cascade_hotreload_info(&el.children);
  86. }
  87. BodyNode::Component(comp) => {
  88. comp.children.template_idx.set(self.next_template_idx());
  89. self.cascade_hotreload_info(&comp.children.roots);
  90. }
  91. BodyNode::ForLoop(floop) => {
  92. floop.body.template_idx.set(self.next_template_idx());
  93. self.cascade_hotreload_info(&floop.body.roots);
  94. }
  95. BodyNode::IfChain(chain) => chain.for_each_branch(&mut |body| {
  96. body.template_idx.set(self.next_template_idx());
  97. self.cascade_hotreload_info(&body.roots)
  98. }),
  99. _ => {}
  100. }
  101. }
  102. }
  103. fn next_template_idx(&self) -> usize {
  104. let idx = self.template_idx.get();
  105. self.template_idx.set(idx + 1);
  106. idx
  107. }
  108. }