Put parsing details into one module

This commit is contained in:
Yuriy Dupyn 2024-01-26 19:45:15 +01:00
parent 61c0a34253
commit 6000b1f242
10 changed files with 30 additions and 29 deletions

View file

@ -0,0 +1,109 @@
use minisql::{operation::Operation, schema::{ColumnName, TableSchema}, type_system::DbType};
use nom::{
bytes::complete::tag,
character::complete::{char, multispace0, multispace1},
multi::separated_list0,
sequence::terminated,
IResult, combinator::opt,
};
use super::common::{parse_table_name, parse_identifier, parse_db_type};
pub fn parse_create(input: &str) -> IResult<&str, Operation> {
let (input, _) = tag("CREATE")(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag("TABLE")(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = parse_table_name(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('(')(input)?;
let (input, _) = multispace0(input)?;
let (input, column_definitions) = parse_column_definitions(input)?;
let mut column_name_position_mapping = Vec::new();
let mut types: Vec<DbType> = Vec::new();
let mut primary_key = None;
for (position, (column_name, db_type, pk)) in column_definitions.iter().enumerate() {
types.push(db_type.clone());
if *pk {
primary_key = Some(position);
}
column_name_position_mapping.push((column_name.clone(), position));
}
let (input, _) = char(')')(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(';')(input)?;
let schema = TableSchema::new(
table_name.to_string(),
primary_key.unwrap_or_default(),
column_name_position_mapping,
types
);
Ok((
input,
Operation::CreateTable(table_name.to_string(), schema),
))
}
pub fn parse_column_definitions(input: &str) -> IResult<&str, Vec<(ColumnName, DbType, bool)>> {
separated_list0(terminated(char(','), multispace0), parse_column_definition)(input)
}
fn parse_primary_key(input: &str) -> IResult<&str, &str> {
let (input, _) = multispace1(input)?;
let (input, _) = tag("PRIMARY")(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag("KEY")(input)?;
Ok((input, "PRIMARY KEY"))
}
pub fn parse_column_definition(input: &str) -> IResult<&str, (ColumnName, DbType, bool)> {
let (input, identifier) = parse_identifier(input)?;
let (input, _) = multispace1(input)?;
let (input, db_type) = parse_db_type(input)?;
let (input, pk) = opt(parse_primary_key)(input).map(|(input, pk)| (input, pk.is_some()))?;
let (input, _) = multispace0(input)?;
Ok((input, (identifier.to_string(), db_type, pk)))
}
#[cfg(test)]
mod tests {
use minisql::operation::Operation;
use crate::parsing::create::parse_create;
#[test]
fn test_parse_create_no_spaces() {
parse_create("CREATE TABLE \"Table1\"(id UUID ,column1 INT);").expect("should parse");
}
#[test]
fn test_parse_create_primary_key() {
parse_create("CREATE TABLE \"Table1\"(id UUID PRIMARY KEY,column1 INT);").expect("should parse");
}
#[test]
fn test_parse_create_no_quotes_table_name() {
parse_create("CREATE TABLE Table1(id UUID PRIMARY KEY,column1 INT);").expect("should parse");
}
#[test]
fn test_parse_create_primary_key_with_spaces() {
parse_create("CREATE TABLE \"Table1\" ( id UUID PRIMARY KEY , column1 INT ) ;").expect("should parse");
}
#[test]
fn test_parse_create() {
let (_, create) = parse_create("CREATE TABLE \"Table1\"( id UUID , column1 INT );").expect("should parse");
assert!(matches!(create, Operation::CreateTable(_ ,_)));
match create {
Operation::CreateTable(name, schema) => {
assert_eq!(name, "Table1");
assert_eq!(schema.number_of_columns(), 2);
assert_eq!(schema.column_position_from_column_name(&"id".to_string()).unwrap(), 0);
assert_eq!(schema.column_position_from_column_name(&"column1".to_string()).unwrap(), 1);
}
_ => {}
}
}
}