uqa_sql/plpgsql/
options.rs1#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub enum VariableConflict {
12 #[default]
14 Error,
15 UseVariable,
17 UseColumn,
19}
20
21impl VariableConflict {
22 #[must_use]
24 pub fn from_name(name: &str) -> Option<Self> {
25 match name.to_ascii_lowercase().as_str() {
26 "error" => Some(Self::Error),
27 "use_variable" => Some(Self::UseVariable),
28 "use_column" => Some(Self::UseColumn),
29 _ => None,
30 }
31 }
32}
33
34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
36pub struct CompileOptions {
37 pub variable_conflict: Option<VariableConflict>,
39 pub print_strict_params: Option<bool>,
41}
42
43#[must_use]
45pub fn compile_options(body: &str) -> CompileOptions {
46 let mut options = CompileOptions::default();
47 let mut rest = skip_space_and_comments(body);
48 while let Some(option) = rest.strip_prefix('#') {
49 let (name, after_name) = read_word(skip_space_and_comments(option));
50 let (value, after_value) = read_word(skip_space_and_comments(after_name));
51 match name.to_ascii_lowercase().as_str() {
52 "variable_conflict" => {
53 options.variable_conflict = VariableConflict::from_name(value);
54 }
55 "print_strict_params" => {
56 options.print_strict_params = match value.to_ascii_lowercase().as_str() {
57 "on" => Some(true),
58 "off" => Some(false),
59 _ => options.print_strict_params,
60 };
61 }
62 _ => {}
64 }
65 rest = skip_space_and_comments(after_value);
66 }
67 options
68}
69
70fn skip_space_and_comments(text: &str) -> &str {
72 let mut rest = text.trim_start();
73 loop {
74 if let Some(comment) = rest.strip_prefix("--") {
75 rest = comment
76 .find('\n')
77 .map_or("", |end| &comment[end..])
78 .trim_start();
79 } else if rest.starts_with("/*") {
80 rest = skip_block_comment(rest).trim_start();
81 } else {
82 return rest;
83 }
84 }
85}
86
87fn skip_block_comment(text: &str) -> &str {
89 let mut depth = 0usize;
90 let mut index = 0;
91 let bytes = text.as_bytes();
92 while index + 1 < bytes.len() {
93 match (bytes[index], bytes[index + 1]) {
94 (b'/', b'*') => {
95 depth += 1;
96 index += 2;
97 }
98 (b'*', b'/') => {
99 depth -= 1;
100 index += 2;
101 if depth == 0 {
102 return &text[index..];
103 }
104 }
105 _ => index += 1,
106 }
107 }
108 ""
109}
110
111fn read_word(text: &str) -> (&str, &str) {
113 let end = text
114 .find(|character: char| !(character.is_ascii_alphanumeric() || character == '_'))
115 .unwrap_or(text.len());
116 text.split_at(end)
117}
118
119#[cfg(test)]
120mod tests;