Skip to main content

code_moniker_core/core/moniker/
query.rs

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}