#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum VariableConflict {
#[default]
Error,
UseVariable,
UseColumn,
}
impl VariableConflict {
#[must_use]
pub fn from_name(name: &str) -> Option<Self> {
match name.to_ascii_lowercase().as_str() {
"error" => Some(Self::Error),
"use_variable" => Some(Self::UseVariable),
"use_column" => Some(Self::UseColumn),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CompileOptions {
pub variable_conflict: Option<VariableConflict>,
pub print_strict_params: Option<bool>,
}
#[must_use]
pub fn compile_options(body: &str) -> CompileOptions {
let mut options = CompileOptions::default();
let mut rest = skip_space_and_comments(body);
while let Some(option) = rest.strip_prefix('#') {
let (name, after_name) = read_word(skip_space_and_comments(option));
let (value, after_value) = read_word(skip_space_and_comments(after_name));
match name.to_ascii_lowercase().as_str() {
"variable_conflict" => {
options.variable_conflict = VariableConflict::from_name(value);
}
"print_strict_params" => {
options.print_strict_params = match value.to_ascii_lowercase().as_str() {
"on" => Some(true),
"off" => Some(false),
_ => options.print_strict_params,
};
}
_ => {}
}
rest = skip_space_and_comments(after_value);
}
options
}
fn skip_space_and_comments(text: &str) -> &str {
let mut rest = text.trim_start();
loop {
if let Some(comment) = rest.strip_prefix("--") {
rest = comment
.find('\n')
.map_or("", |end| &comment[end..])
.trim_start();
} else if rest.starts_with("/*") {
rest = skip_block_comment(rest).trim_start();
} else {
return rest;
}
}
}
fn skip_block_comment(text: &str) -> &str {
let mut depth = 0usize;
let mut index = 0;
let bytes = text.as_bytes();
while index + 1 < bytes.len() {
match (bytes[index], bytes[index + 1]) {
(b'/', b'*') => {
depth += 1;
index += 2;
}
(b'*', b'/') => {
depth -= 1;
index += 2;
if depth == 0 {
return &text[index..];
}
}
_ => index += 1,
}
}
""
}
fn read_word(text: &str) -> (&str, &str) {
let end = text
.find(|character: char| !(character.is_ascii_alphanumeric() || character == '_'))
.unwrap_or(text.len());
text.split_at(end)
}
#[cfg(test)]
mod tests;