uqa_sql/semantics/parameters/
identifier_list.rs1use super::units::is_c_space;
10
11const MAX_IDENTIFIER_BYTES: usize = 63;
13
14pub fn split_identifier_list(text: &str, separator: u8) -> Option<Vec<String>> {
16 let bytes = text.as_bytes();
17 let mut index = skip_space(bytes, 0);
18 let mut names = Vec::new();
19 if index == bytes.len() {
20 return Some(names);
21 }
22 loop {
23 let name = if bytes.get(index) == Some(&b'"') {
24 let mut name = Vec::new();
25 index += 1;
26 loop {
27 let quote = index + bytes[index..].iter().position(|byte| *byte == b'"')?;
28 name.extend_from_slice(&bytes[index..quote]);
29 if bytes.get(quote + 1) == Some(&b'"') {
30 name.push(b'"');
31 index = quote + 2;
32 } else {
33 index = quote + 1;
34 break;
35 }
36 }
37 String::from_utf8(name).expect("split at ASCII quotes")
38 } else {
39 let start = index;
40 while index < bytes.len() && bytes[index] != separator && !is_c_space(bytes[index]) {
41 index += 1;
42 }
43 if index == start {
44 return None;
45 }
46 text[start..index].to_ascii_lowercase()
47 };
48 index = skip_space(bytes, index);
49 let done = match bytes.get(index) {
50 Some(byte) if *byte == separator => {
51 index = skip_space(bytes, index + 1);
52 false
53 }
54 None => true,
55 Some(_) => return None,
56 };
57 names.push(truncate(name));
58 if done {
59 return Some(names);
60 }
61 }
62}
63
64fn skip_space(bytes: &[u8], mut index: usize) -> usize {
65 while bytes.get(index).copied().is_some_and(is_c_space) {
66 index += 1;
67 }
68 index
69}
70
71fn truncate(mut name: String) -> String {
72 if name.len() > MAX_IDENTIFIER_BYTES {
73 let mut end = MAX_IDENTIFIER_BYTES;
74 while !name.is_char_boundary(end) {
75 end -= 1;
76 }
77 name.truncate(end);
78 }
79 name
80}
81
82#[cfg(test)]
83mod tests;