use super::ast::FileCTESpec;
use super::lexer::Token;
pub struct FileCteParser;
impl FileCteParser {
pub fn parse(parser: &mut crate::sql::recursive_parser::Parser) -> Result<FileCTESpec, String> {
if let Token::Identifier(id) = &parser.current_token {
if id.to_uppercase() != "PATH" {
return Err(format!("Expected PATH keyword in FILE CTE, found '{}'", id));
}
} else {
return Err("Expected PATH keyword in FILE CTE".to_string());
}
parser.advance();
let path = match &parser.current_token {
Token::StringLiteral(p) => p.clone(),
_ => return Err("Expected string literal after PATH keyword".to_string()),
};
parser.advance();
let mut recursive = false;
let mut glob: Option<String> = None;
let mut max_depth: Option<usize> = None;
let mut max_files: Option<usize> = None;
let mut follow_links = false;
let mut include_hidden = false;
while !matches!(parser.current_token, Token::RightParen)
&& !matches!(parser.current_token, Token::Eof)
{
if let Token::Identifier(id) = &parser.current_token {
match id.to_uppercase().as_str() {
"RECURSIVE" => {
parser.advance();
recursive = true;
}
"GLOB" => {
parser.advance();
glob = Some(Self::parse_string(parser, "GLOB")?);
}
"MAX_DEPTH" => {
parser.advance();
max_depth = Some(Self::parse_usize(parser, "MAX_DEPTH")?);
}
"MAX_FILES" => {
parser.advance();
max_files = Some(Self::parse_usize(parser, "MAX_FILES")?);
}
"FOLLOW_LINKS" => {
parser.advance();
follow_links = true;
}
"INCLUDE_HIDDEN" => {
parser.advance();
include_hidden = true;
}
_ => {
return Err(format!(
"Unexpected keyword '{}' in FILE CTE specification",
id
));
}
}
} else {
break;
}
}
Ok(FileCTESpec {
path,
recursive,
glob,
max_depth,
max_files,
follow_links,
include_hidden,
})
}
fn parse_string(
parser: &mut crate::sql::recursive_parser::Parser,
clause: &str,
) -> Result<String, String> {
match &parser.current_token {
Token::StringLiteral(s) => {
let s = s.clone();
parser.advance();
Ok(s)
}
_ => Err(format!("Expected string literal after {} clause", clause)),
}
}
fn parse_usize(
parser: &mut crate::sql::recursive_parser::Parser,
clause: &str,
) -> Result<usize, String> {
match &parser.current_token {
Token::NumberLiteral(n) => {
let val = n
.parse::<usize>()
.map_err(|_| format!("Invalid {} value: {}", clause, n))?;
parser.advance();
Ok(val)
}
_ => Err(format!("Expected integer after {} clause", clause)),
}
}
}