runmat_runtime/call/
identity.rs1use runmat_types::{CallableIdentity, QualifiedName, SymbolName};
2
3pub fn external_qualified_identity(base: &str, member: &str) -> CallableIdentity {
5 let base_segments = {
6 let split = base.split('.').collect::<Vec<_>>();
7 if !split.is_empty() && split.iter().all(|segment| !segment.is_empty()) {
8 split
9 .into_iter()
10 .map(|segment| SymbolName(segment.to_string()))
11 .collect::<Vec<_>>()
12 } else {
13 vec![SymbolName(base.to_string())]
14 }
15 };
16 let mut segments = base_segments;
17 segments.push(SymbolName(member.to_string()));
18 CallableIdentity::ExternalName(QualifiedName(segments))
19}
20
21pub fn strict_callable_display_name(identity: &CallableIdentity) -> Option<String> {
23 match identity {
24 CallableIdentity::BoundFunction(_) | CallableIdentity::AnonymousFunction(_) => None,
25 CallableIdentity::ExternalFunction { display_name, .. } => {
26 (!display_name.is_empty()).then_some(display_name.clone())
27 }
28 CallableIdentity::Builtin(id) => (!id.0.is_empty()).then_some(id.0.clone()),
29 CallableIdentity::Imported(path) => path.module.display_name(),
30 CallableIdentity::Method(id) => (!id.0.is_empty()).then_some(id.0.clone()),
31 CallableIdentity::DynamicName(name) => (!name.0.is_empty()).then_some(name.0.clone()),
32 CallableIdentity::ExternalName(QualifiedName(segments)) => {
33 if segments.is_empty() || segments.iter().any(|segment| segment.0.is_empty()) {
34 return None;
35 }
36 Some(
37 segments
38 .iter()
39 .map(|segment| segment.0.as_str())
40 .collect::<Vec<_>>()
41 .join("."),
42 )
43 }
44 }
45}
46
47pub fn external_qualified_display_name(base: &str, member: &str) -> String {
48 strict_callable_display_name(&external_qualified_identity(base, member))
49 .expect("external qualified identity should always have a display name")
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn qualified_identity_preserves_valid_segments() {
58 let CallableIdentity::ExternalName(QualifiedName(segments)) =
59 external_qualified_identity("pkg.Point", "origin")
60 else {
61 panic!("expected external name");
62 };
63 assert_eq!(
64 segments,
65 vec![
66 SymbolName("pkg".into()),
67 SymbolName("Point".into()),
68 SymbolName("origin".into())
69 ]
70 );
71 }
72
73 #[test]
74 fn malformed_base_remains_one_lossless_segment() {
75 let CallableIdentity::ExternalName(QualifiedName(segments)) =
76 external_qualified_identity("pkg..Point", "origin")
77 else {
78 panic!("expected external name");
79 };
80 assert_eq!(
81 segments,
82 vec![SymbolName("pkg..Point".into()), SymbolName("origin".into())]
83 );
84 }
85}