miden_assembly_syntax/ast/path/
join.rs1use alloc::{boxed::Box, string::String, sync::Arc};
2
3use super::*;
4use crate::ast;
5
6pub trait Join<T: ?Sized>: sealed::Joinable {
14 fn join(&self, other: &T) -> PathBuf;
28}
29
30mod sealed {
31 #[doc(hidden)]
32 pub trait Joinable {}
33
34 impl Joinable for crate::ast::Path {}
35 impl Joinable for crate::ast::PathBuf {}
36}
37
38impl Join<Path> for Path {
39 fn join(&self, other: &Path) -> PathBuf {
40 if other.is_empty() {
41 return self.to_path_buf();
42 }
43
44 if self.is_empty() {
45 other.to_path_buf()
46 } else if other.is_absolute() || other.is_in_kernel() || other.is_in_exec() {
47 match other.to_absolute() {
48 Ok(path) => path.into_owned(),
49 Err(_) => other.to_path_buf(),
50 }
51 } else {
52 let mut buf = self.to_path_buf();
53 buf.push(other);
54
55 buf
56 }
57 }
58}
59
60impl Join<PathBuf> for Path {
61 #[inline(always)]
62 fn join(&self, other: &PathBuf) -> PathBuf {
63 <Path as Join<Path>>::join(self, other.as_path())
64 }
65}
66
67impl Join<str> for Path {
68 fn join(&self, other: &str) -> PathBuf {
69 let mut buf = self.to_path_buf();
70 buf.push_component(other);
71 buf
72 }
73}
74
75impl Join<String> for Path {
76 fn join(&self, other: &String) -> PathBuf {
77 <Path as Join<str>>::join(self, other)
78 }
79}
80
81impl Join<Box<str>> for Path {
82 fn join(&self, other: &Box<str>) -> PathBuf {
83 <Path as Join<str>>::join(self, other)
84 }
85}
86
87impl Join<Arc<str>> for Path {
88 fn join(&self, other: &Arc<str>) -> PathBuf {
89 <Path as Join<str>>::join(self, other)
90 }
91}
92
93impl Join<ast::Ident> for Path {
94 fn join(&self, other: &ast::Ident) -> PathBuf {
95 <Path as Join<str>>::join(self, other.as_str())
96 }
97}
98
99impl Join<ast::ProcedureName> for Path {
100 fn join(&self, other: &ast::ProcedureName) -> PathBuf {
101 <Path as Join<str>>::join(self, other.as_str())
102 }
103}
104
105impl Join<ast::QualifiedProcedureName> for Path {
106 fn join(&self, other: &ast::QualifiedProcedureName) -> PathBuf {
107 let mut buf = <Path as Join<Path>>::join(self, other.namespace());
108 buf.push_component(other.name());
109 buf
110 }
111}
112
113impl Join<Path> for PathBuf {
114 #[inline]
115 fn join(&self, other: &Path) -> PathBuf {
116 <Path as Join<Path>>::join(self.as_path(), other)
117 }
118}
119
120impl Join<PathBuf> for PathBuf {
121 #[inline(always)]
122 fn join(&self, other: &PathBuf) -> PathBuf {
123 <Path as Join<Path>>::join(self.as_path(), other.as_path())
124 }
125}
126
127impl Join<str> for PathBuf {
128 fn join(&self, other: &str) -> PathBuf {
129 <Path as Join<str>>::join(self.as_path(), other)
130 }
131}
132
133impl Join<String> for PathBuf {
134 fn join(&self, other: &String) -> PathBuf {
135 <Path as Join<str>>::join(self.as_path(), other)
136 }
137}
138
139impl Join<Box<str>> for PathBuf {
140 fn join(&self, other: &Box<str>) -> PathBuf {
141 <Path as Join<str>>::join(self.as_path(), other)
142 }
143}
144
145impl Join<Arc<str>> for PathBuf {
146 fn join(&self, other: &Arc<str>) -> PathBuf {
147 <Path as Join<str>>::join(self.as_path(), other)
148 }
149}
150
151impl Join<ast::Ident> for PathBuf {
152 fn join(&self, other: &ast::Ident) -> PathBuf {
153 <Path as Join<ast::Ident>>::join(self.as_path(), other)
154 }
155}
156
157impl Join<ast::ProcedureName> for PathBuf {
158 fn join(&self, other: &ast::ProcedureName) -> PathBuf {
159 <Path as Join<ast::ProcedureName>>::join(self.as_path(), other)
160 }
161}
162
163impl Join<ast::QualifiedProcedureName> for PathBuf {
164 fn join(&self, other: &ast::QualifiedProcedureName) -> PathBuf {
165 <Path as Join<ast::QualifiedProcedureName>>::join(self.as_path(), other)
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use core::assert_matches;
172
173 use super::*;
174
175 #[test]
176 fn test_join_path_to_path_plain() {
177 let p1 = Path::new("foo");
178 let p2 = Path::new("bar::baz");
179 let joined = Join::join(p1, p2);
180 assert_eq!(joined.as_path(), Path::new("foo::bar::baz"));
181 let mut components = joined.components();
182 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
183 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
184 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("baz"))));
185 assert_matches!(components.next(), None);
186 }
187
188 #[test]
189 fn test_join_absolute_path_to_path_plain() {
190 let p1 = Path::new("foo");
191 let p2 = Path::new("::bar::baz");
192 let joined = Join::join(p1, p2);
193 assert_eq!(joined.as_path(), Path::new("::bar::baz"));
194 let mut components = joined.components();
195 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
196 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
197 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("baz"))));
198 assert_matches!(components.next(), None);
199 }
200
201 #[test]
202 fn test_join_invalid_absolute_path_does_not_panic() {
203 let p1 = Path::new("foo");
204 let invalid = alloc::format!("::{}", "a".repeat(Path::MAX_COMPONENT_LENGTH + 1));
205 let p2 = Path::new(&invalid);
206
207 let joined = Join::join(p1, p2);
208
209 assert_eq!(joined.as_path(), p2);
210 }
211
212 #[test]
213 fn test_join_path_to_path_quoted() {
214 let p1 = Path::new("foo");
215 let p2 = Path::new("\"bar::baz\"::qux");
216 let joined = Join::join(p1, p2);
217 assert_eq!(joined.as_path(), Path::new("foo::\"bar::baz\"::qux"));
218 let mut components = joined.components();
219 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
220 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar::baz\""))));
221 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("qux"))));
222 assert_matches!(components.next(), None);
223 }
224
225 #[test]
226 fn test_join_path_to_absolute_path_quoted() {
227 let p1 = Path::new("::foo");
228 let p2 = Path::new("\"bar::baz\"::qux");
229 let joined = Join::join(p1, p2);
230 assert_eq!(joined.as_path(), Path::new("::foo::\"bar::baz\"::qux"));
231 let mut components = joined.components();
232 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
233 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
234 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar::baz\""))));
235 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("qux"))));
236 assert_matches!(components.next(), None);
237 }
238
239 #[test]
240 fn test_join_str_to_path_simple() {
241 let p1 = Path::new("foo");
242 let joined = Join::join(p1, "bar");
243 assert_eq!(joined.as_path(), Path::new("foo::bar"));
244 let mut components = joined.components();
245 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
246 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
247 assert_matches!(components.next(), None);
248 }
249
250 #[test]
251 fn test_join_str_to_path_multi_component_quoted() {
252 let p1 = Path::new("foo");
253 let joined = Join::join(p1, "\"bar::baz\"");
254 assert_eq!(joined.as_path(), Path::new("foo::\"bar::baz\""));
255 let mut components = joined.components();
256 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
257 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar::baz\""))));
258 assert_matches!(components.next(), None);
259 }
260
261 #[test]
262 fn test_join_str_to_path_multi_component_unquoted() {
263 let p1 = Path::new("foo");
264 let joined = Join::join(p1, "bar::baz");
265 assert_eq!(joined.as_path(), Path::new("foo::\"bar::baz\""));
266 let mut components = joined.components();
267 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
268 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar::baz\""))));
269 assert_matches!(components.next(), None);
270 }
271
272 #[test]
273 fn test_join_qualified_proc_name_to_path() {
274 let p1 = Path::new("foo");
275 let proc = ast::ProcedureName::new("qux").unwrap();
276 let p2 = ast::QualifiedProcedureName::new("bar::baz", proc);
277 let joined = Join::join(p1, &p2);
278 assert_eq!(joined.as_path(), Path::new("foo::bar::baz::qux"));
279 let mut components = joined.components();
280 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
281 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
282 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("baz"))));
283 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("qux"))));
284 assert_matches!(components.next(), None);
285 }
286}