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