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::type_system::DbType; use crate::syntax::Condition; 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> { 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::type_system::DbType; use crate::syntax::Condition; 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(_))); } }