1use super::{Moniker, MonikerBuilder};
2
3impl Moniker {
4 pub fn is_ancestor_of(&self, other: &Moniker) -> bool {
5 self.as_view().is_ancestor_of(&other.as_view())
6 }
7
8 pub fn parent(&self) -> Option<Moniker> {
9 let view = self.as_view();
10 let n = view.segment_count() as usize;
11 if n == 0 {
12 return None;
13 }
14 let mut b = MonikerBuilder::from_view(view);
15 b.truncate(n - 1);
16 Some(b.build())
17 }
18
19 pub fn last_kind(&self) -> Option<Vec<u8>> {
20 self.as_view().segments().last().map(|s| s.kind.to_vec())
21 }
22
23 pub fn bind_match(&self, other: &Moniker) -> bool {
24 self.as_view().bind_match(&other.as_view())
25 }
26
27 pub fn with_bare_last_segment(&self) -> Moniker {
28 let view = self.as_view();
29 let segs: Vec<_> = view.segments().collect();
30 let Some(last) = segs.last() else {
31 return self.clone();
32 };
33 let bare = bare_callable_name(last.name);
34 if bare.len() == last.name.len() {
35 return self.clone();
36 }
37 let mut b = MonikerBuilder::new();
38 b.project(view.project());
39 for s in &segs[..segs.len() - 1] {
40 b.segment(s.kind, s.name);
41 }
42 b.segment(last.kind, bare);
43 b.build()
44 }
45}
46
47impl<'a> super::MonikerView<'a> {
48 pub fn bind_match(&self, other: &super::MonikerView<'_>) -> bool {
49 if self.project() != other.project() {
50 return false;
51 }
52 let mut ls = self.segments();
53 let mut rs = other.segments();
54 let mut prev_l = match ls.next() {
55 Some(s) => s,
56 None => return false,
57 };
58 let mut prev_r = match rs.next() {
59 Some(s) => s,
60 None => return false,
61 };
62 loop {
63 match (ls.next(), rs.next()) {
64 (None, None) => return last_segment_match(self, other, prev_l.name, prev_r.name),
65 (Some(_), None) | (None, Some(_)) => return false,
66 (Some(nl), Some(nr)) => {
67 if prev_l != prev_r {
68 return false;
69 }
70 prev_l = nl;
71 prev_r = nr;
72 }
73 }
74 }
75 }
76
77 pub fn lang_segment(&self) -> Option<&[u8]> {
78 self.segments().find(|s| s.kind == b"lang").map(|s| s.name)
79 }
80}
81
82fn last_segment_match(
83 left: &super::MonikerView<'_>,
84 right: &super::MonikerView<'_>,
85 l_name: &[u8],
86 r_name: &[u8],
87) -> bool {
88 if l_name == r_name {
89 return true;
90 }
91 let l_lang = left.lang_segment();
92 let r_lang = right.lang_segment();
93 if let Some(lang) = l_lang
94 && l_lang == r_lang
95 {
96 #[allow(clippy::match_same_arms)]
97 match lang {
98 b"sql" => return bare_callable_name(l_name) == bare_callable_name(r_name),
99 b"ts" => return bare_callable_name(l_name) == bare_callable_name(r_name),
100 b"java" => return bare_callable_name(l_name) == bare_callable_name(r_name),
101 b"python" => return bare_callable_name(l_name) == bare_callable_name(r_name),
102 b"rs" => return bare_callable_name(l_name) == bare_callable_name(r_name),
103 b"go" => return bare_callable_name(l_name) == bare_callable_name(r_name),
104 b"cs" => return bare_callable_name(l_name) == bare_callable_name(r_name),
105 _ => {}
106 }
107 }
108 false
109}
110
111pub fn bare_callable_name(name: &[u8]) -> &[u8] {
112 match name.iter().position(|b| *b == b'(') {
113 Some(i) => &name[..i],
114 None => name,
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 fn mk(project: &[u8], segs: &[(&[u8], &[u8])]) -> Moniker {
123 let mut b = MonikerBuilder::new();
124 b.project(project);
125 for (k, name) in segs {
126 b.segment(k, name);
127 }
128 b.build()
129 }
130
131 #[test]
132 fn ancestor_is_reflexive() {
133 let m = mk(b"app", &[(b"path", b"a"), (b"path", b"b")]);
134 assert!(m.is_ancestor_of(&m));
135 }
136
137 #[test]
138 fn ancestor_of_strict_prefix() {
139 let parent = mk(b"app", &[(b"path", b"a")]);
140 let child = mk(b"app", &[(b"path", b"a"), (b"path", b"b")]);
141 assert!(parent.is_ancestor_of(&child));
142 assert!(!child.is_ancestor_of(&parent));
143 }
144
145 #[test]
146 fn ancestor_rejects_different_project() {
147 let a = mk(b"app1", &[(b"path", b"x")]);
148 let b = mk(b"app2", &[(b"path", b"x"), (b"path", b"y")]);
149 assert!(!a.is_ancestor_of(&b));
150 }
151
152 #[test]
153 fn ancestor_rejects_diverging_segment() {
154 let a = mk(b"app", &[(b"path", b"a"), (b"path", b"b")]);
155 let b = mk(b"app", &[(b"path", b"a"), (b"path", b"c")]);
156 assert!(!a.is_ancestor_of(&b));
157 }
158
159 #[test]
160 fn parent_drops_last_segment() {
161 let m = mk(b"app", &[(b"path", b"a"), (b"path", b"b")]);
162 let p = m.parent().unwrap();
163 let expected = mk(b"app", &[(b"path", b"a")]);
164 assert_eq!(p, expected);
165 }
166
167 #[test]
168 fn parent_of_project_only_is_none() {
169 let m = mk(b"app", &[]);
170 assert!(m.parent().is_none());
171 }
172
173 #[test]
174 fn parent_of_one_segment_is_project_only() {
175 let m = mk(b"app", &[(b"path", b"a")]);
176 let p = m.parent().unwrap();
177 assert_eq!(p.as_view().segment_count(), 0);
178 assert_eq!(p.as_view().project(), b"app");
179 }
180
181 #[test]
182 fn last_kind_returns_kind_of_last_segment() {
183 let m = mk(b"app", &[(b"path", b"a"), (b"class", b"Foo")]);
184 assert_eq!(m.last_kind(), Some(b"class".to_vec()));
185 }
186
187 #[test]
188 fn last_kind_is_none_for_project_only() {
189 let m = mk(b"app", &[]);
190 assert!(m.last_kind().is_none());
191 }
192
193 #[test]
194 fn byte_lex_is_tree_friendly() {
195 let m1 = mk(b"app", &[(b"class", b"Foo")]);
196 let descendant = mk(b"app", &[(b"class", b"Foo"), (b"method", b"bar()")]);
197 let deeper = mk(
198 b"app",
199 &[(b"class", b"Foo"), (b"method", b"bar()"), (b"path", b"x")],
200 );
201 let sibling = mk(b"app", &[(b"class", b"Zoo")]);
202
203 assert!(m1.as_bytes() < descendant.as_bytes());
204 assert!(descendant.as_bytes() < deeper.as_bytes());
205 assert!(deeper.as_bytes() < sibling.as_bytes());
206 }
207
208 #[test]
209 fn descendant_with_longer_name_stays_inside_parent_range() {
210 let parent = mk(b"app", &[(b"class", b"Foo")]);
211 let child_long = mk(
212 b"app",
213 &[
214 (b"class", b"Foo"),
215 (b"method", b"bar_longer_than_anything()"),
216 ],
217 );
218 let next_sibling = mk(b"app", &[(b"class", b"Zoo")]);
219
220 assert!(parent.as_bytes() < child_long.as_bytes());
221 assert!(child_long.as_bytes() < next_sibling.as_bytes());
222 }
223
224 #[test]
225 fn bind_match_equal_monikers_match() {
226 let m = mk(b"app", &[(b"class", b"Foo")]);
227 assert!(m.bind_match(&m));
228 }
229
230 #[test]
231 fn bind_match_path_vs_class_on_last_segment_matches() {
232 let import = mk(b"app", &[(b"module", b"util"), (b"path", b"Foo")]);
233 let def = mk(b"app", &[(b"module", b"util"), (b"class", b"Foo")]);
234 assert!(import.bind_match(&def));
235 assert!(def.bind_match(&import));
236 }
237
238 #[test]
239 fn bind_match_rejects_different_projects() {
240 let l = mk(b"app1", &[(b"class", b"Foo")]);
241 let r = mk(b"app2", &[(b"class", b"Foo")]);
242 assert!(!l.bind_match(&r));
243 }
244
245 #[test]
246 fn bind_match_rejects_different_lang_segment() {
247 let l = mk(b"app", &[(b"lang", b"python"), (b"class", b"Foo")]);
248 let r = mk(b"app", &[(b"lang", b"java"), (b"class", b"Foo")]);
249 assert!(!l.bind_match(&r));
250 }
251
252 #[test]
253 fn bind_match_rejects_different_parent_segment_kind() {
254 let l = mk(b"app", &[(b"package", b"acme"), (b"class", b"Foo")]);
255 let r = mk(b"app", &[(b"path", b"acme"), (b"class", b"Foo")]);
256 assert!(!l.bind_match(&r));
257 }
258
259 #[test]
260 fn bind_match_rejects_different_segment_count() {
261 let shallow = mk(b"app", &[(b"class", b"Foo")]);
262 let deep = mk(b"app", &[(b"class", b"Foo"), (b"method", b"bar()")]);
263 assert!(!shallow.bind_match(&deep));
264 }
265
266 #[test]
267 fn bind_match_rejects_project_only_monikers() {
268 let l = mk(b"app", &[]);
269 let r = mk(b"app", &[]);
270 assert!(!l.bind_match(&r));
271 }
272
273 #[test]
274 fn bind_match_rejects_different_last_segment_name() {
275 let l = mk(b"app", &[(b"class", b"Foo")]);
276 let r = mk(b"app", &[(b"class", b"Bar")]);
277 assert!(!l.bind_match(&r));
278 }
279
280 #[test]
281 fn bind_match_sql_arity_call_matches_typed_def() {
282 let typed_def = mk(
283 b"app",
284 &[
285 (b"lang", b"sql"),
286 (b"schema", b"esac"),
287 (b"module", b"plan"),
288 (b"function", b"create_plan(uuid,text)"),
289 ],
290 );
291 let arity_call = mk(
292 b"app",
293 &[
294 (b"lang", b"sql"),
295 (b"schema", b"esac"),
296 (b"module", b"plan"),
297 (b"function", b"create_plan(2)"),
298 ],
299 );
300 assert!(arity_call.bind_match(&typed_def));
301 assert!(typed_def.bind_match(&arity_call));
302 }
303
304 #[test]
305 fn bind_match_sql_bare_name_matches_typed_def() {
306 let typed_def = mk(
307 b"app",
308 &[
309 (b"lang", b"sql"),
310 (b"module", b"plan"),
311 (b"function", b"create_plan(uuid,text)"),
312 ],
313 );
314 let bare_call = mk(
315 b"app",
316 &[
317 (b"lang", b"sql"),
318 (b"module", b"plan"),
319 (b"function", b"create_plan"),
320 ],
321 );
322 assert!(bare_call.bind_match(&typed_def));
323 }
324
325 #[test]
326 fn bind_match_sql_different_bare_names_do_not_match() {
327 let a = mk(
328 b"app",
329 &[
330 (b"lang", b"sql"),
331 (b"module", b"plan"),
332 (b"function", b"create_plan(uuid)"),
333 ],
334 );
335 let b = mk(
336 b"app",
337 &[
338 (b"lang", b"sql"),
339 (b"module", b"plan"),
340 (b"function", b"drop_plan(uuid)"),
341 ],
342 );
343 assert!(!a.bind_match(&b));
344 }
345
346 #[test]
347 fn bind_match_ts_typed_def_matches_arity_call() {
348 let typed_def = mk(
349 b"app",
350 &[
351 (b"lang", b"ts"),
352 (b"module", b"util"),
353 (b"function", b"foo(number)"),
354 ],
355 );
356 let arity_call = mk(
357 b"app",
358 &[
359 (b"lang", b"ts"),
360 (b"module", b"util"),
361 (b"function", b"foo(1)"),
362 ],
363 );
364 assert!(typed_def.bind_match(&arity_call));
365 assert!(arity_call.bind_match(&typed_def));
366 }
367
368 #[test]
369 fn bind_match_ts_typed_def_matches_bare_read() {
370 let typed_def = mk(
371 b"app",
372 &[
373 (b"lang", b"ts"),
374 (b"module", b"util"),
375 (b"function", b"foo(number,number)"),
376 ],
377 );
378 let bare_read = mk(
379 b"app",
380 &[
381 (b"lang", b"ts"),
382 (b"module", b"util"),
383 (b"function", b"foo()"),
384 ],
385 );
386 assert!(typed_def.bind_match(&bare_read));
387 }
388
389 #[test]
390 fn bind_match_ts_distinct_bare_names_do_not_match() {
391 let foo = mk(
392 b"app",
393 &[
394 (b"lang", b"ts"),
395 (b"module", b"util"),
396 (b"function", b"foo(number)"),
397 ],
398 );
399 let bar = mk(
400 b"app",
401 &[
402 (b"lang", b"ts"),
403 (b"module", b"util"),
404 (b"function", b"bar(1)"),
405 ],
406 );
407 assert!(!foo.bind_match(&bar));
408 }
409
410 #[test]
411 fn bind_match_java_typed_def_matches_arity_call() {
412 let typed_def = mk(
413 b"app",
414 &[
415 (b"lang", b"java"),
416 (b"package", b"acme"),
417 (b"class", b"Plan"),
418 (b"method", b"create(int)"),
419 ],
420 );
421 let arity_call = mk(
422 b"app",
423 &[
424 (b"lang", b"java"),
425 (b"package", b"acme"),
426 (b"class", b"Plan"),
427 (b"method", b"create(1)"),
428 ],
429 );
430 assert!(typed_def.bind_match(&arity_call));
431 }
432
433 #[test]
434 fn bind_match_java_typed_def_matches_bare_read() {
435 let typed_def = mk(
436 b"app",
437 &[
438 (b"lang", b"java"),
439 (b"package", b"acme"),
440 (b"class", b"Plan"),
441 (b"method", b"create(int)"),
442 ],
443 );
444 let bare = mk(
445 b"app",
446 &[
447 (b"lang", b"java"),
448 (b"package", b"acme"),
449 (b"class", b"Plan"),
450 (b"method", b"create"),
451 ],
452 );
453 assert!(typed_def.bind_match(&bare));
454 }
455
456 #[test]
457 fn bind_match_python_typed_def_matches_arity_call() {
458 let typed_def = mk(
459 b"app",
460 &[
461 (b"lang", b"python"),
462 (b"module", b"m"),
463 (b"function", b"f(int)"),
464 ],
465 );
466 let arity_call = mk(
467 b"app",
468 &[
469 (b"lang", b"python"),
470 (b"module", b"m"),
471 (b"function", b"f(1)"),
472 ],
473 );
474 assert!(typed_def.bind_match(&arity_call));
475 }
476
477 #[test]
478 fn bind_match_rust_typed_def_matches_arity_call() {
479 let typed_def = mk(
480 b"app",
481 &[
482 (b"lang", b"rs"),
483 (b"module", b"util"),
484 (b"function", b"add(i32,i32)"),
485 ],
486 );
487 let arity_call = mk(
488 b"app",
489 &[
490 (b"lang", b"rs"),
491 (b"module", b"util"),
492 (b"function", b"add(2)"),
493 ],
494 );
495 assert!(typed_def.bind_match(&arity_call));
496 }
497
498 #[test]
499 fn bind_match_csharp_typed_def_matches_arity_call() {
500 let typed_def = mk(
501 b"app",
502 &[
503 (b"lang", b"cs"),
504 (b"namespace", b"Acme"),
505 (b"class", b"Plan"),
506 (b"method", b"Add(int,int)"),
507 ],
508 );
509 let arity_call = mk(
510 b"app",
511 &[
512 (b"lang", b"cs"),
513 (b"namespace", b"Acme"),
514 (b"class", b"Plan"),
515 (b"method", b"Add(2)"),
516 ],
517 );
518 assert!(typed_def.bind_match(&arity_call));
519 assert!(arity_call.bind_match(&typed_def));
520 }
521
522 #[test]
523 fn bind_match_csharp_typed_constructor_matches_arity_call() {
524 let typed_def = mk(
525 b"app",
526 &[
527 (b"lang", b"cs"),
528 (b"namespace", b"Acme"),
529 (b"class", b"Plan"),
530 (b"constructor", b"Plan(int,string)"),
531 ],
532 );
533 let arity_call = mk(
534 b"app",
535 &[
536 (b"lang", b"cs"),
537 (b"namespace", b"Acme"),
538 (b"class", b"Plan"),
539 (b"constructor", b"Plan(2)"),
540 ],
541 );
542 assert!(typed_def.bind_match(&arity_call));
543 }
544
545 #[test]
546 fn bind_match_csharp_distinct_bare_names_do_not_match() {
547 let foo = mk(
548 b"app",
549 &[
550 (b"lang", b"cs"),
551 (b"class", b"Plan"),
552 (b"method", b"Add(int)"),
553 ],
554 );
555 let bar = mk(
556 b"app",
557 &[
558 (b"lang", b"cs"),
559 (b"class", b"Plan"),
560 (b"method", b"Subtract(1)"),
561 ],
562 );
563 assert!(!foo.bind_match(&bar));
564 }
565}