123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- //! The actual rsx! macro implementation.
- //!
- //! This mostly just defers to the root TemplateBody with some additional tooling to provide better errors.
- //! Currently the additional tooling doesn't do much.
- use proc_macro2::TokenStream as TokenStream2;
- use quote::ToTokens;
- use std::{cell::Cell, fmt::Debug};
- use syn::{
- parse::{Parse, ParseStream},
- Result,
- };
- use crate::{BodyNode, TemplateBody};
- /// The Callbody is the contents of the rsx! macro
- ///
- /// It is a list of BodyNodes, which are the different parts of the template.
- /// The Callbody contains no information about how the template will be rendered, only information about the parsed tokens.
- ///
- /// Every callbody should be valid, so you can use it to build a template.
- /// To generate the code used to render the template, use the ToTokens impl on the Callbody, or with the `render_with_location` method.
- ///
- /// 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.
- #[derive(Debug, Clone)]
- pub struct CallBody {
- pub body: TemplateBody,
- pub template_idx: Cell<usize>,
- }
- impl Parse for CallBody {
- fn parse(input: ParseStream) -> Result<Self> {
- // Defer to the `new` method such that we can wire up hotreload information
- Ok(CallBody::new(input.parse()?))
- }
- }
- impl ToTokens for CallBody {
- fn to_tokens(&self, out: &mut TokenStream2) {
- self.body.to_tokens(out)
- }
- }
- impl CallBody {
- /// Create a new CallBody from a TemplateBody
- ///
- /// This will overwrite all internal metadata regarding hotreloading.
- pub fn new(body: TemplateBody) -> Self {
- let body = CallBody {
- body,
- template_idx: Cell::new(0),
- };
- body.body.template_idx.set(body.next_template_idx());
- body.cascade_hotreload_info(&body.body.roots);
- body
- }
- /// Parse a stream into a CallBody. Return all error immediately instead of trying to partially expand the macro
- ///
- /// This should be preferred over `parse` if you are outside of a macro
- pub fn parse_strict(input: ParseStream) -> Result<Self> {
- // todo: actually throw warnings if there are any
- Self::parse(input)
- }
- /// With the entire knowledge of the macro call, wire up location information for anything hotreloading
- /// specific. It's a little bit simpler just to have a global id per callbody than to try and track it
- /// relative to each template, though we could do that if we wanted to.
- ///
- /// For now this is just information for ifmts and templates so that when they generate, they can be
- /// tracked back to the original location in the source code, to support formatted string hotreloading.
- ///
- /// Note that there are some more complex cases we could in theory support, but have bigger plans
- /// to enable just pure rust hotreloading that would make those tricks moot. So, manage more of
- /// the simple cases until that proper stuff ships.
- ///
- /// We need to make sure to wire up:
- /// - subtemplate IDs
- /// - ifmt IDs
- /// - dynamic node IDs
- /// - dynamic attribute IDs
- /// - paths for dynamic nodes and attributes
- ///
- /// Lots of wiring!
- ///
- /// However, here, we only need to wire up template IDs since TemplateBody will handle the rest.
- ///
- /// This is better though since we can save the relevant data on the structures themselves.
- fn cascade_hotreload_info(&self, nodes: &[BodyNode]) {
- for node in nodes.iter() {
- match node {
- BodyNode::Element(el) => {
- self.cascade_hotreload_info(&el.children);
- }
- BodyNode::Component(comp) => {
- comp.children.template_idx.set(self.next_template_idx());
- self.cascade_hotreload_info(&comp.children.roots);
- }
- BodyNode::ForLoop(floop) => {
- floop.body.template_idx.set(self.next_template_idx());
- self.cascade_hotreload_info(&floop.body.roots);
- }
- BodyNode::IfChain(chain) => chain.for_each_branch(&mut |body| {
- body.template_idx.set(self.next_template_idx());
- self.cascade_hotreload_info(&body.roots)
- }),
- _ => {}
- }
- }
- }
- fn next_template_idx(&self) -> usize {
- let idx = self.template_idx.get();
- self.template_idx.set(idx + 1);
- idx
- }
- }
|