95 lines
3.2 KiB
Rust
95 lines
3.2 KiB
Rust
use nom::{
|
|
character::complete::{alphanumeric1, char, multispace0, anychar, multispace1},
|
|
combinator::peek,
|
|
error::make_error,
|
|
sequence::{delimited, terminated},
|
|
bytes::complete::tag,
|
|
IResult, branch::alt,
|
|
};
|
|
use minisql::{operation::Condition, type_system::DbType};
|
|
|
|
use super::literal::parse_db_value;
|
|
|
|
pub fn parse_table_name(input: &str) -> IResult<&str, &str> {
|
|
alt((
|
|
delimited(char('"'), alphanumeric1, char('"')),
|
|
parse_identifier
|
|
))(input)
|
|
}
|
|
|
|
pub fn parse_identifier(input: &str) -> IResult<&str, &str> {
|
|
// TODO: allow underscores
|
|
let (_, first) = peek(anychar)(input)?;
|
|
if first.is_alphabetic() {
|
|
alphanumeric1(input)
|
|
} else {
|
|
Err(nom::Err::Error(make_error(input, nom::error::ErrorKind::Alpha)))
|
|
}
|
|
}
|
|
|
|
pub fn parse_column_name(input: &str) -> IResult<&str, String> {
|
|
terminated(parse_identifier, multispace0)(input).map(|(rest, name)| (rest, name.to_string()))
|
|
}
|
|
|
|
pub fn parse_db_type(input: &str) -> IResult<&str, DbType> {
|
|
let (input, type_name) = alt((tag("STRING"), tag("INT"), tag("NUMBER"), tag("UUID")))(input)?;
|
|
let db_type = match type_name {
|
|
"STRING" => DbType::String,
|
|
"INT" => DbType::Int,
|
|
"UUID" => DbType::Uuid,
|
|
"NUMBER" => DbType::Number,
|
|
_ => return Err(nom::Err::Failure(make_error(input, nom::error::ErrorKind::IsNot)))
|
|
};
|
|
Ok((input, db_type))
|
|
}
|
|
|
|
pub fn parse_condition(input: &str) -> IResult<&str, Option<Condition>> {
|
|
match tag::<&str, &str, nom::error::Error<&str>>("WHERE")(input) {
|
|
Ok((input, _)) => {
|
|
let (input, _) = multispace1(input)?;
|
|
let (input, condition) = parse_equality(input)?;
|
|
Ok((input, Some(condition)))
|
|
}
|
|
Err(_) => {
|
|
Ok((input, None))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn parse_equality(input: &str) -> IResult<&str, Condition> {
|
|
let (input, column_name) = parse_column_name(input)?;
|
|
let (input, _) = multispace0(input)?;
|
|
let (input, _) = char('=')(input)?;
|
|
let (input, _) = multispace0(input)?;
|
|
let (input, db_value) = parse_db_value(input)?;
|
|
Ok((input, Condition::Eq(column_name, db_value)))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use minisql::{operation::Condition, type_system::DbType};
|
|
use crate::parsing::common::{parse_db_type, parse_equality};
|
|
|
|
#[test]
|
|
fn test_parse_equality() {
|
|
use minisql::type_system::{IndexableValue, Value};
|
|
match parse_equality("id = 1") {
|
|
Ok(("", Condition::Eq(column_name, value))) => {
|
|
assert!(column_name.eq("id"));
|
|
assert_eq!(value, Value::Indexable(IndexableValue::Int(1)))
|
|
}
|
|
_ => {
|
|
panic!("should parse");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_db_type() {
|
|
assert!(matches!(parse_db_type("INT").expect("should parse").1, DbType::Int));
|
|
assert!(matches!(parse_db_type("STRING").expect("should parse").1, DbType::String));
|
|
assert!(matches!(parse_db_type("UUID").expect("should parse").1, DbType::Uuid));
|
|
assert!(matches!(parse_db_type("NUMBER").expect("should parse").1, DbType::Number));
|
|
assert!(matches!(parse_db_type("Unknown"), Err(_)));
|
|
}
|
|
}
|