use minisql::type_system::{IndexableValue, Value}; use nom::{ branch::alt, character::complete::{u64, char, digit1, none_of}, combinator::opt, multi::many0, sequence::{delimited, pair, preceded}, IResult, error::make_error }; pub fn parse_db_value(input: &str) -> IResult<&str, Value> { alt(( parse_string, parse_number, parse_int, parse_uuid, ))(input) } pub fn parse_number(input: &str) -> IResult<&str, Value> { // Parse the integer part let (input, (sign, digits)) = pair(opt(char('-')), digit1)(input)?; // Parse the fractional part let (input, frac_part) = opt(pair(char('.'), digit1))(input)?; match frac_part { Some((_fsign, fdigits)) => { // Combine integer and fractional parts let combined_parts = format!( "{}{}.{}", sign.unwrap_or('+'), digits, fdigits ); // Parse the combined parts as a floating-point number let value = combined_parts.parse::() .map_err(|_| { nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail)) })?; Ok((input, Value::Number(value))) } None => { let value = format!("{}{}", sign.unwrap_or('+'), digits).parse::() .map_err(|_| { nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail)) })?; Ok((input, Value::Indexable(IndexableValue::Int(value)))) } } } pub fn parse_int(input: &str) -> IResult<&str, Value> { u64(input).map(|(input, v)| { (input, Value::Indexable(IndexableValue::Int(v))) }) } fn escape_tab(input:&str) -> IResult<&str, char> { let (input, _) = preceded(char('\\'), char('t'))(input)?; Ok((input, '\t')) } fn escape_backslash(input:&str) -> IResult<&str, char> { let (input, _) = preceded(char('\\'), char('\\'))(input)?; Ok((input, '\\')) } fn escape_newline(input:&str) -> IResult<&str, char> { let (input, _) = preceded(char('\\'), char('n'))(input)?; Ok((input, '\n')) } fn escape_carriegereturn(input:&str) -> IResult<&str, char> { let (input, _) = preceded(char('\\'), char('r'))(input)?; Ok((input, '\r')) } fn escape_doublequote(input:&str) -> IResult<&str, char> { preceded(char('\\'), char('"'))(input) } pub fn parse_string(input: &str) -> IResult<&str, Value> { // Parse the content inside the double quotes let (input, content) = delimited( char('"'), many0(alt(( escape_backslash, escape_carriegereturn, escape_newline, escape_doublequote, escape_tab, none_of(r#"\""#) ))), char('"'), )(input)?; // Combine the characters into a string let value: String = content.into_iter().collect(); Ok((input, Value::Indexable(IndexableValue::String(value)))) } pub fn parse_uuid(input: &str) -> IResult<&str, Value> { let (input, value) = pair(char('u'), u64)(input).map(|(input, (_, v))| { (input, Value::Indexable(IndexableValue::Uuid(v))) })?; Ok((input, value)) } #[cfg(test)] mod tests { use minisql::type_system::{IndexableValue, Value}; use crate::parsing::literal::{parse_db_value, parse_string, parse_uuid}; #[test] fn test_string_parser() { assert_eq!(parse_string(r#""simple""#), Ok(("", Value::Indexable(IndexableValue::String(String::from("simple")))))); assert_eq!(parse_string(r#""\"\t\r\n\\""#), Ok(("", Value::Indexable(IndexableValue::String(String::from("\"\t\r\n\\")))))); assert_eq!(parse_string(r#""name is \"John\".""#), Ok(("", Value::Indexable(IndexableValue::String(String::from("name is \"John\".")))))); } #[test] fn test_parse_db_value() { let (input, value) = parse_db_value("5").expect("should parse"); assert_eq!(input, ""); assert_eq!(value, Value::Indexable(IndexableValue::Int(5))); let (input, value) = parse_db_value("5.5").expect("should parse"); assert_eq!(input, ""); assert_eq!(value, Value::Number(5.5)); let (_, _) = parse_db_value("\"STRING\"").expect("should parse"); let (input, value) = parse_db_value("\"abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ \"").expect("should parse"); assert_eq!(input, ""); assert_eq!(value, Value::Indexable(IndexableValue::String("abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ ".to_string()))); } #[test] fn test_parse_positive_float() { assert_eq!(parse_db_value("23.213313"), Ok(("", Value::Number(23.213313)))); assert_eq!(parse_db_value("2241.9734"), Ok(("", Value::Number(2241.9734)))); } #[test] fn test_parse_negative_float() { assert_eq!(parse_db_value("-9241.873654"), Ok(("", Value::Number(-9241.873654)))); assert_eq!(parse_db_value("-62625.0"), Ok(("", Value::Number(-62625.0)))); } #[test] fn test_parse_float_between_0_and_1() { assert_eq!(parse_db_value("0.873654"), Ok(("", Value::Number(0.873654)))); assert_eq!(parse_db_value("0.62625"), Ok(("", Value::Number(0.62625)))); } #[test] fn test_parse_int() { assert_eq!(parse_db_value("5134616"), Ok(("", Value::Indexable(IndexableValue::Int(5134616))))); } #[test] fn test_parse_uuid() { assert_eq!(parse_uuid("u131515"), Ok(("", Value::Indexable(IndexableValue::Uuid(131515))))) } }