flowscope_core/analyzer/helpers/
naming.rs1use crate::types::CanonicalName;
2
3pub fn extract_simple_name(name: &str) -> String {
4 let mut parts = split_qualified_identifiers(name);
5 parts.pop().unwrap_or_else(|| name.to_string())
6}
7
8pub fn split_qualified_identifiers(name: &str) -> Vec<String> {
9 let mut parts = Vec::new();
10 let mut current = String::new();
11 let mut chars = name.chars().peekable();
12 let mut active_quote: Option<char> = None;
13
14 while let Some(ch) = chars.next() {
15 if let Some(q) = active_quote {
16 current.push(ch);
17 if ch == q {
18 if matches!(q, '"' | '\'' | '`') {
19 if let Some(next) = chars.peek() {
20 if *next == q {
21 current.push(chars.next().unwrap());
22 continue;
23 }
24 }
25 }
26 active_quote = None;
27 } else if q == ']' && ch == ']' {
28 active_quote = None;
29 }
30 continue;
31 }
32
33 match ch {
34 '"' | '\'' | '`' => {
35 active_quote = Some(ch);
36 current.push(ch);
37 }
38 '[' => {
39 active_quote = Some(']');
40 current.push(ch);
41 }
42 '.' => {
43 if !current.is_empty() {
44 parts.push(current.trim().to_string());
45 current.clear();
46 }
47 }
48 _ => current.push(ch),
49 }
50 }
51
52 if !current.is_empty() {
53 parts.push(current.trim().to_string());
54 }
55
56 if parts.is_empty() && !name.is_empty() {
57 vec![name.trim().to_string()]
58 } else {
59 parts
60 }
61}
62
63pub fn is_quoted_identifier(part: &str) -> bool {
64 let trimmed = part.trim();
65 if trimmed.len() < 2 {
66 return false;
67 }
68 let first = trimmed.chars().next().unwrap();
69 let last = trimmed.chars().last().unwrap();
70 matches!(
71 (first, last),
72 ('"', '"') | ('`', '`') | ('[', ']') | ('\'', '\'')
73 )
74}
75
76pub fn unquote_identifier(part: &str) -> String {
77 let trimmed = part.trim();
78 if trimmed.len() < 2 {
79 return trimmed.to_string();
80 }
81
82 if is_quoted_identifier(trimmed) {
83 trimmed[1..trimmed.len() - 1].to_string()
84 } else {
85 trimmed.to_string()
86 }
87}
88
89pub fn parse_canonical_name(name: &str) -> CanonicalName {
90 let parts = split_qualified_identifiers(name);
91 match parts.len() {
92 0 => CanonicalName::table(None, None, String::new()),
93 1 => CanonicalName::table(None, None, parts[0].clone()),
94 2 => CanonicalName::table(None, Some(parts[0].clone()), parts[1].clone()),
95 3 => CanonicalName::table(
96 Some(parts[0].clone()),
97 Some(parts[1].clone()),
98 parts[2].clone(),
99 ),
100 _ => CanonicalName::table(None, None, name.to_string()),
101 }
102}