Find `IN 'collection'` clause position in uppercase MATCH text. Accepts `)` or whitespace before `IN` (unlike `find_top_level_keyword` which only accepts whitespace). Only matches at top-level (depth == 0).
(upper: &str)
| 38 | /// Accepts `)` or whitespace before `IN` (unlike `find_top_level_keyword` |
| 39 | /// which only accepts whitespace). Only matches at top-level (depth == 0). |
| 40 | pub(super) fn find_in_clause(upper: &str) -> Option<usize> { |
| 41 | let mut depth = 0i32; |
| 42 | let bytes = upper.as_bytes(); |
| 43 | let len = upper.len(); |
| 44 | |
| 45 | for i in 0..len.saturating_sub(1) { |
| 46 | match bytes[i] { |
| 47 | b'(' | b'[' | b'{' => depth += 1, |
| 48 | b')' | b']' | b'}' => depth -= 1, |
| 49 | _ => {} |
| 50 | } |
| 51 | |
| 52 | if depth == 0 |
| 53 | && upper[i..].starts_with("IN") |
| 54 | && (i == 0 || bytes[i - 1].is_ascii_whitespace() || bytes[i - 1] == b')') |
| 55 | && (i + 2 >= len || bytes[i + 2].is_ascii_whitespace()) |
| 56 | { |
| 57 | return Some(i); |
| 58 | } |
| 59 | } |
| 60 | None |
| 61 | } |
| 62 | |
| 63 | /// Find the next MATCH or OPTIONAL MATCH keyword in text. |
| 64 | pub(super) fn find_next_match_keyword(upper: &str) -> Option<usize> { |