1#[macro_export]
16macro_rules! template {
17 (backend = $backend:literal, $path:literal) => {
18 include!(concat!(
19 env!("OUT_DIR"),
20 "/fusor_backends/",
21 $backend,
22 "/",
23 $path,
24 ".rs"
25 ));
26 };
27 ($path:literal) => {
28 include!(concat!(env!("OUT_DIR"), "/fusor_templates/", $path, ".rs"));
29 };
30}
31
32#[macro_export]
42macro_rules! bindings {
43 ($name:ident) => {
44 const __FUSOR_BINDINGS_ORIGIN: (&str, &str) = (file!(), env!("CARGO_MANIFEST_DIR"));
45 include!(concat!(
46 env!("OUT_DIR"),
47 "/fusor_",
48 stringify!($name),
49 ".rs"
50 ));
51 };
52}
53
54#[doc(hidden)]
57pub const fn source_matches(origin: (&str, &str), expected: &str) -> bool {
58 let file = native_path(origin.0.as_bytes());
59 let manifest = native_path(origin.1.as_bytes());
60 let expected = PathParts(b"", native_path(expected.as_bytes()));
61 if same_path(PathParts(b"", file), expected) {
62 return true;
63 }
64 let mut end = manifest.len();
68 loop {
69 if same_path(PathParts(manifest.split_at(end).0, file), expected) {
70 return true;
71 }
72 if end == 0 {
73 return false;
74 }
75 end -= 1;
76 while end > 0 && !separator(manifest[end]) {
77 end -= 1;
78 }
79 }
80}
81
82const fn native_path(path: &[u8]) -> &[u8] {
85 if path.len() >= 4
86 && separator(path[0])
87 && separator(path[1])
88 && path[2] == b'?'
89 && separator(path[3])
90 {
91 let path = path.split_at(4).1;
92 if path.len() >= 4
93 && path[0] == b'U'
94 && path[1] == b'N'
95 && path[2] == b'C'
96 && separator(path[3])
97 {
98 return path.split_at(4).1;
99 }
100 return path;
101 }
102 path
103}
104
105const fn separator(byte: u8) -> bool {
106 byte == b'/' || byte == b'\\'
107}
108
109#[derive(Clone, Copy)]
110struct PathParts<'a>(&'a [u8], &'a [u8]);
111impl PathParts<'_> {
112 const fn len(self) -> usize {
113 self.0.len() + 1 + self.1.len()
114 }
115 const fn at(self, index: usize) -> u8 {
116 if index < self.0.len() {
117 self.0[index]
118 } else if index == self.0.len() {
119 b'/'
120 } else {
121 self.1[index - self.0.len() - 1]
122 }
123 }
124}
125
126const fn previous(path: PathParts<'_>, mut end: usize) -> Option<(usize, usize)> {
127 let mut parents = 0;
128 while end > 0 {
129 while end > 0 && separator(path.at(end - 1)) {
130 end -= 1;
131 }
132 if end == 0 {
133 break;
134 }
135 let mut start = end;
136 while start > 0 && !separator(path.at(start - 1)) {
137 start -= 1;
138 }
139 if end - start == 2 && path.at(start) == b'.' && path.at(start + 1) == b'.' {
140 parents += 1;
141 } else if end - start == 1 && path.at(start) == b'.' {
142 } else if parents > 0 {
143 parents -= 1;
144 } else {
145 return Some((start, end));
146 }
147 end = start;
148 }
149 None
150}
151
152const fn same_path(left: PathParts<'_>, right: PathParts<'_>) -> bool {
153 let mut a = left.len();
154 let mut b = right.len();
155 loop {
156 match (previous(left, a), previous(right, b)) {
157 (None, None) => return true,
158 (Some((start_a, end_a)), Some((start_b, end_b))) => {
159 if end_a - start_a != end_b - start_b {
160 return false;
161 }
162 let mut index = 0;
163 while index < end_a - start_a {
164 if left.at(start_a + index) != right.at(start_b + index) {
165 return false;
166 }
167 index += 1;
168 }
169 a = start_a;
170 b = start_b;
171 }
172 _ => return false,
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::source_matches;
180
181 #[test]
182 fn source_identity_handles_cargo_and_native_module_paths() {
183 for (origin, expected) in [
184 (("src/app.rs", "/app"), "/app/src/app.rs"),
185 (("/app/src/app.rs", "/app"), "/app/src/app.rs"),
186 (
187 ("examples/app/src/app.rs", "/workspace/examples/app"),
188 "/workspace/examples/app/web/../src/app.rs",
189 ),
190 (("src/../web/page.rs", "/app"), "/app/web/page.rs"),
191 (("src\\app.rs", "C:\\app"), "C:/app/src/app.rs"),
192 (("src\\app.rs", "C:\\app"), "\\\\?\\C:\\app\\src\\app.rs"),
193 (
194 ("src\\app.rs", "\\\\server\\app"),
195 "\\\\?\\UNC\\server\\app\\src\\app.rs",
196 ),
197 (
198 ("./src/nested/../../web/page.rs", "/app"),
199 "/app/web/page.rs",
200 ),
201 ] {
202 assert!(source_matches(origin, expected));
203 assert!(!source_matches(origin, "/another/page.rs"));
204 assert!(!source_matches(origin, "/app/other/src/app.rs"));
205 }
206 }
207}