use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
use syn::visit_mut::{self, VisitMut};
use crate::migrate_v2::rewriter::FileRewriter;
pub struct Rule;
impl FileRewriter for Rule {
fn name(&self) -> &'static str {
"watch_unwrap"
}
fn rewrite(&self, mut file: syn::File) -> syn::File {
PageMacroBodyVisitor.visit_file_mut(&mut file);
file
}
}
struct PageMacroBodyVisitor;
impl VisitMut for PageMacroBodyVisitor {
fn visit_macro_mut(&mut self, m: &mut syn::Macro) {
if m.path
.segments
.last()
.map(|s| s.ident == "page")
.unwrap_or(false)
{
m.tokens = unwrap_watch(m.tokens.clone());
}
visit_mut::visit_macro_mut(self, m);
}
}
fn unwrap_watch(input: TokenStream) -> TokenStream {
let mut out: Vec<TokenTree> = Vec::new();
let mut iter = input.into_iter().peekable();
while let Some(tt) = iter.next() {
match &tt {
TokenTree::Ident(id) if id == "watch" => {
if let Some(TokenTree::Group(g)) = iter.peek()
&& g.delimiter() == Delimiter::Brace
{
let body = match iter.next() {
Some(TokenTree::Group(g)) => g.stream(),
_ => unreachable!("peek matched but next did not"),
};
out.extend(unwrap_watch(body));
continue;
}
out.push(tt);
}
TokenTree::Punct(p) if p.as_char() == '#' => {
let mut lookahead = iter.clone();
let reactive_with_brace = matches!(
(lookahead.next(), lookahead.next()),
(Some(TokenTree::Ident(id2)), Some(TokenTree::Group(g)))
if id2 == "reactive" && g.delimiter() == Delimiter::Brace
);
if reactive_with_brace {
let _ = iter.next(); let body = match iter.next() {
Some(TokenTree::Group(g)) => g.stream(),
_ => unreachable!("lookahead matched but next did not"),
};
out.extend(unwrap_watch(body));
continue;
}
out.push(tt);
}
TokenTree::Group(g) if g.delimiter() == Delimiter::Brace => {
let inner = unwrap_watch(g.stream());
out.push(TokenTree::Group(Group::new(Delimiter::Brace, inner)));
}
_ => out.push(tt),
}
}
out.into_iter().collect()
}