dioxus_rsx_hotreload/
collect.rs1use syn::visit_mut::VisitMut;
12use syn::{File, Macro};
13
14pub struct ChangedRsx {
15 pub old: Macro,
17
18 pub new: Macro,
20}
21
22#[derive(Debug)]
23pub enum ReloadableRustCode {
24 Rsx { old: Macro, new: Macro },
25}
26
27pub fn diff_rsx(new: &File, old: &File) -> Option<Vec<ChangedRsx>> {
36 let mut old = old.clone();
38 let mut new = new.clone();
39
40 let old_macros = collect_from_file(&mut old);
42 let new_macros = collect_from_file(&mut new);
43
44 if old_macros.len() != new_macros.len() {
46 return None;
47 }
48
49 if old != new {
51 return None;
52 }
53
54 Some(
55 old_macros
56 .into_iter()
57 .zip(new_macros)
58 .map(|(old, new)| ChangedRsx { old, new })
59 .collect(),
60 )
61}
62
63pub fn collect_from_file(file: &mut File) -> Vec<Macro> {
64 struct MacroCollector(Vec<Macro>);
65 impl VisitMut for MacroCollector {
66 fn visit_macro_mut(&mut self, dest: &mut syn::Macro) {
68 let name = &dest.path.segments.last().map(|i| i.ident.to_string());
69 if let Some("rsx" | "render") = name.as_deref() {
70 let mut default: syn::Macro = syn::parse_quote! { rsx! {} };
71 std::mem::swap(dest, &mut default);
72 self.0.push(default)
73 }
74 }
75
76 fn visit_attribute_mut(&mut self, i: &mut syn::Attribute) {
78 if i.path().is_ident("doc") {
79 *i = syn::parse_quote! { #[doc = ""] };
80 }
81 }
82 }
83
84 let mut macros = MacroCollector(vec![]);
85 macros.visit_file_mut(file);
86 macros.0
87}
88
89#[test]
90fn changing_files() {
91 let old = r#"
92use dioxus::prelude::*;
93
94/// some comment
95pub fn CoolChild() -> Element {
96 let a = 123;
97
98 rsx! {
99 div {
100 {some_expr()}
101 }
102 }
103}"#;
104
105 let new = r#"
106use dioxus::prelude::*;
107
108/// some comment
109pub fn CoolChild() -> Element {
110 rsx! {
111 div {
112 {some_expr()}
113 }
114 }
115}"#;
116
117 let same = r#"
118use dioxus::prelude::*;
119
120/// some comment!!!!!
121pub fn CoolChild() -> Element {
122 let a = 123;
123
124 rsx! {
125 div {
126 {some_expr()}
127 }
128 }
129}"#;
130
131 let old = syn::parse_file(old).unwrap();
132 let new = syn::parse_file(new).unwrap();
133 let same = syn::parse_file(same).unwrap();
134
135 assert!(
136 diff_rsx(&old, &new).is_none(),
137 "Files with different expressions should not be hotreloadable"
138 );
139
140 assert!(
141 diff_rsx(&new, &new).is_some(),
142 "The same file should be reloadable with itself"
143 );
144
145 assert!(
146 diff_rsx(&old, &same).is_some(),
147 "Files with changed comments should be hotreloadable"
148 );
149}