cargo format

This commit is contained in:
Yuriy Dupyn 2024-01-28 22:40:41 +01:00
parent 4d45da0cd1
commit 845db102c2
33 changed files with 885 additions and 530 deletions

View file

@ -1,20 +1,21 @@
use minisql::type_system::DbType;
use nom::{
character::complete::{alphanumeric1, char, multispace0, anychar, multispace1},
branch::alt,
bytes::complete::tag,
character::complete::{alphanumeric1, anychar, char, multispace0, multispace1},
combinator::peek,
error::make_error,
sequence::{delimited, terminated},
bytes::complete::tag,
IResult, branch::alt,
IResult,
};
use minisql::type_system::DbType;
use crate::syntax::Condition;
use super::literal::parse_db_value;
use crate::syntax::Condition;
pub fn parse_table_name(input: &str) -> IResult<&str, &str> {
alt((
delimited(char('"'), alphanumeric1, char('"')),
parse_identifier
parse_identifier,
))(input)
}
@ -24,7 +25,10 @@ pub fn parse_identifier(input: &str) -> IResult<&str, &str> {
if first.is_alphabetic() {
alphanumeric1(input)
} else {
Err(nom::Err::Error(make_error(input, nom::error::ErrorKind::Alpha)))
Err(nom::Err::Error(make_error(
input,
nom::error::ErrorKind::Alpha,
)))
}
}
@ -39,7 +43,12 @@ pub fn parse_db_type(input: &str) -> IResult<&str, DbType> {
"INT" => DbType::Int,
"UUID" => DbType::Uuid,
"NUMBER" => DbType::Number,
_ => return Err(nom::Err::Failure(make_error(input, nom::error::ErrorKind::IsNot)))
_ => {
return Err(nom::Err::Failure(make_error(
input,
nom::error::ErrorKind::IsNot,
)))
}
};
Ok((input, db_type))
}
@ -51,9 +60,7 @@ pub fn parse_condition(input: &str) -> IResult<&str, Option<Condition>> {
let (input, condition) = parse_equality(input)?;
Ok((input, Some(condition)))
}
Err(_) => {
Ok((input, None))
}
Err(_) => Ok((input, None)),
}
}
@ -70,9 +77,9 @@ fn parse_equality(input: &str) -> IResult<&str, Condition> {
mod tests {
use minisql::type_system::DbType;
use crate::syntax::Condition;
use crate::parsing::common::{parse_db_type, parse_equality};
use crate::syntax::Condition;
#[test]
fn test_parse_equality() {
use minisql::type_system::{IndexableValue, Value};
@ -89,10 +96,22 @@ mod tests {
#[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("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(_)));
}
}

View file

@ -1,13 +1,14 @@
use nom::{
bytes::complete::tag,
character::complete::{char, multispace0, multispace1},
combinator::opt,
multi::separated_list0,
sequence::terminated,
IResult, combinator::opt,
IResult,
};
use super::common::{parse_table_name, parse_identifier, parse_db_type};
use crate::syntax::{RawTableSchema, ColumnSchema, RawQuerySyntax};
use super::common::{parse_db_type, parse_identifier, parse_table_name};
use crate::syntax::{ColumnSchema, RawQuerySyntax, RawTableSchema};
pub fn parse_create(input: &str) -> IResult<&str, RawQuerySyntax> {
let (input, _) = tag("CREATE")(input)?;
@ -27,10 +28,7 @@ pub fn parse_create(input: &str) -> IResult<&str, RawQuerySyntax> {
table_name: table_name.to_string(),
columns: column_definitions,
};
Ok((
input,
RawQuerySyntax::CreateTable(schema),
))
Ok((input, RawQuerySyntax::CreateTable(schema)))
}
fn parse_column_definitions(input: &str) -> IResult<&str, Vec<ColumnSchema>> {
@ -51,7 +49,14 @@ fn parse_column_definition(input: &str) -> IResult<&str, ColumnSchema> {
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, ColumnSchema { column_name: identifier.to_string(), type_: db_type, is_primary: pk }))
Ok((
input,
ColumnSchema {
column_name: identifier.to_string(),
type_: db_type,
is_primary: pk,
},
))
}
#[cfg(test)]
@ -66,22 +71,28 @@ mod tests {
#[test]
fn test_parse_create_primary_key() {
parse_create("CREATE TABLE \"Table1\"(id UUID PRIMARY KEY,column1 INT);").expect("should parse");
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");
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");
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");
let (_, create) = parse_create("CREATE TABLE \"Table1\"( id UUID , column1 INT );")
.expect("should parse");
assert!(matches!(create, RawQuerySyntax::CreateTable(_)));
match create {
RawQuerySyntax::CreateTable(schema) => {
@ -95,7 +106,9 @@ mod tests {
let result_column1 = schema.get_column(&"column1".to_string());
assert!(matches!(result_column1, Some(_)));
let Some(column1_column) = result_column1 else { panic!() };
let Some(column1_column) = result_column1 else {
panic!()
};
assert_eq!(column1_column.column_name, "column1".to_string());
}
_ => {}

View file

@ -4,8 +4,8 @@ use nom::{
IResult,
};
use super::common::{parse_condition, parse_table_name};
use crate::syntax::RawQuerySyntax;
use super::common::{parse_table_name, parse_condition};
pub fn parse_delete(input: &str) -> IResult<&str, RawQuerySyntax> {
let (input, _) = tag("DELETE")(input)?;
@ -25,14 +25,15 @@ pub fn parse_delete(input: &str) -> IResult<&str, RawQuerySyntax> {
#[cfg(test)]
mod tests {
use crate::syntax::RawQuerySyntax;
use crate::parsing::delete::parse_delete;
use crate::syntax::RawQuerySyntax;
#[test]
fn test_parse_delete() {
let (_, operation) = parse_delete("DELETE FROM \"T1\" WHERE id = 1 ;").expect("should parse");
let (_, operation) =
parse_delete("DELETE FROM \"T1\" WHERE id = 1 ;").expect("should parse");
assert!(matches!(operation, RawQuerySyntax::Delete(_, _)))
}
// TODO: add test with condition
// TODO: add test with condition
}

View file

@ -2,7 +2,8 @@ use crate::syntax::RawQuerySyntax;
use nom::{
bytes::complete::tag,
character::complete::{char, multispace0, multispace1},
IResult, combinator::opt,
combinator::opt,
IResult,
};
use super::common::{parse_identifier, parse_table_name};
@ -35,16 +36,16 @@ pub fn parse_create_index(input: &str) -> IResult<&str, RawQuerySyntax> {
Ok((input, operation))
}
#[cfg(test)]
mod tests {
use crate::syntax::RawQuerySyntax;
use crate::parsing::index::parse_create_index;
use crate::syntax::RawQuerySyntax;
#[test]
fn test_create_index() {
let (_, syntax) = parse_create_index("CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" (email);").expect("should parse");
let (_, syntax) =
parse_create_index("CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" (email);")
.expect("should parse");
assert!(matches!(syntax, RawQuerySyntax::CreateIndex(_, _)));
match syntax {
RawQuerySyntax::CreateIndex(table_name, column_name) => {
@ -57,7 +58,10 @@ mod tests {
#[test]
fn test_create_index_with_spaces() {
let (_, syntax) = parse_create_index("CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" ( email ) ;").expect("should parse");
let (_, syntax) = parse_create_index(
"CREATE UNIQUE INDEX idxcontactsemail ON \"contacts\" ( email ) ;",
)
.expect("should parse");
assert!(matches!(syntax, RawQuerySyntax::CreateIndex(_, _)));
match syntax {
RawQuerySyntax::CreateIndex(table_name, column_name) => {

View file

@ -1,9 +1,12 @@
use super::{literal::parse_db_value, common::{parse_table_name, parse_identifier}};
use super::{
common::{parse_identifier, parse_table_name},
literal::parse_db_value,
};
use crate::syntax::RawQuerySyntax;
use minisql::type_system::Value;
use nom::{
bytes::complete::tag,
character::complete::{multispace0, multispace1, char},
character::complete::{char, multispace0, multispace1},
combinator::map,
multi::separated_list0,
sequence::terminated,
@ -14,7 +17,7 @@ pub fn parse_insert(input: &str) -> IResult<&str, RawQuerySyntax> {
let (input, _) = tag("INSERT")(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag("INTO")(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = parse_table_name(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = char('(')(input)?;
@ -34,27 +37,31 @@ pub fn parse_insert(input: &str) -> IResult<&str, RawQuerySyntax> {
let (input, _) = char(';')(input)?;
Ok((
input,
RawQuerySyntax::Insert(table_name.to_string(), column_names.into_iter().zip(values).collect()),
RawQuerySyntax::Insert(
table_name.to_string(),
column_names.into_iter().zip(values).collect(),
),
))
}
pub fn parse_columns(input: &str) -> IResult<&str, Vec<String>> {
separated_list0(terminated(char(','), multispace0), map(parse_identifier, |name|name.to_string()))(input)
separated_list0(
terminated(char(','), multispace0),
map(parse_identifier, |name| name.to_string()),
)(input)
}
pub fn parse_values(input: &str) -> IResult<&str, Vec<Value>> {
separated_list0(terminated(char(','), multispace0), parse_db_value)(input)
}
#[cfg(test)]
mod tests {
use minisql::type_system::{IndexableValue, Value};
use crate::syntax::RawQuerySyntax;
use super::parse_insert;
use crate::syntax::RawQuerySyntax;
#[test]
fn test_parse_insert() {
let sql = "INSERT INTO \"MyTable\" (id, data) VALUES(1, \"Text\");";
@ -63,11 +70,15 @@ mod tests {
("", RawQuerySyntax::Insert(table_name, insertion_values)) => {
assert_eq!(table_name, "MyTable");
assert_eq!(
insertion_values,
insertion_values,
vec![
("id".to_string(), Value::Indexable(IndexableValue::Int(1))),
("data".to_string(), Value::Indexable(IndexableValue::String("Text".to_string())))
]);
(
"data".to_string(),
Value::Indexable(IndexableValue::String("Text".to_string()))
)
]
);
}
_ => {
unreachable!()
@ -77,16 +88,22 @@ mod tests {
#[test]
fn test_parse_insert_with_spaces() {
let sql = "INSERT INTO \"MyTable\" ( id, data ) VALUES ( 1, \"Text\" ) ;";
let sql =
"INSERT INTO \"MyTable\" ( id, data ) VALUES ( 1, \"Text\" ) ;";
let operation = parse_insert(sql).expect("should parse");
match operation {
("", RawQuerySyntax::Insert(table_name, insertion_values)) => {
assert_eq!(table_name, "MyTable");
assert_eq!(insertion_values,
assert_eq!(
insertion_values,
vec![
("id".to_string(), Value::Indexable(IndexableValue::Int(1))),
("data".to_string(), Value::Indexable(IndexableValue::String("Text".to_string())))
]);
(
"data".to_string(),
Value::Indexable(IndexableValue::String("Text".to_string()))
)
]
);
}
_ => {
unreachable!()

View file

@ -1,20 +1,16 @@
use minisql::type_system::{IndexableValue, Value};
use nom::{
branch::alt,
character::complete::{u64, char, digit1, none_of},
character::complete::{char, digit1, none_of, u64},
combinator::opt,
error::make_error,
multi::many0,
sequence::{delimited, pair, preceded},
IResult, error::make_error
IResult,
};
pub fn parse_db_value(input: &str) -> IResult<&str, Value> {
alt((
parse_string,
parse_number,
parse_int,
parse_uuid,
))(input)
alt((parse_string, parse_number, parse_int, parse_uuid))(input)
}
pub fn parse_number(input: &str) -> IResult<&str, Value> {
@ -27,56 +23,47 @@ pub fn parse_number(input: &str) -> IResult<&str, Value> {
match frac_part {
Some((_fsign, fdigits)) => {
// Combine integer and fractional parts
let combined_parts = format!(
"{}{}.{}",
sign.unwrap_or('+'),
digits,
fdigits
);
let combined_parts = format!("{}{}.{}", sign.unwrap_or('+'), digits, fdigits);
// Parse the combined parts as a floating-point number
let value = combined_parts.parse::<f64>()
.map_err(|_| {
nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail))
})?;
let value = combined_parts
.parse::<f64>()
.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::<u64>()
.map_err(|_| {
nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail))
})?;
let value = format!("{}{}", sign.unwrap_or('+'), digits)
.parse::<u64>()
.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)))
})
u64(input).map(|(input, v)| (input, Value::Indexable(IndexableValue::Int(v))))
}
fn escape_tab(input:&str) -> IResult<&str, char> {
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> {
fn escape_backslash(input: &str) -> IResult<&str, char> {
let (input, _) = preceded(char('\\'), char('\\'))(input)?;
Ok((input, '\\'))
}
fn escape_newline(input:&str) -> IResult<&str, char> {
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> {
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> {
fn escape_doublequote(input: &str) -> IResult<&str, char> {
preceded(char('\\'), char('"'))(input)
}
@ -90,7 +77,7 @@ pub fn parse_string(input: &str) -> IResult<&str, Value> {
escape_newline,
escape_doublequote,
escape_tab,
none_of(r#"\""#)
none_of(r#"\""#),
))),
char('"'),
)(input)?;
@ -102,23 +89,39 @@ pub fn parse_string(input: &str) -> IResult<&str, 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)))
})?;
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};
use minisql::type_system::{IndexableValue, Value};
#[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\"."))))));
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]
@ -132,39 +135,63 @@ mod tests {
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");
let (input, value) =
parse_db_value("\"abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ \"").expect("should parse");
assert_eq!(input, "");
assert_eq!(value, Value::Indexable(IndexableValue::String("abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ ".to_string())));
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))));
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))));
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.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)))));
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)))))
assert_eq!(
parse_uuid("u131515"),
Ok(("", Value::Indexable(IndexableValue::Uuid(131515))))
)
}
}

View file

@ -1,7 +1,7 @@
pub(crate) mod literal;
pub(crate) mod select;
pub(crate) mod common;
pub(crate) mod create;
pub(crate) mod insert;
pub(crate) mod delete;
pub(crate) mod index;
pub(crate) mod insert;
pub(crate) mod literal;
pub(crate) mod select;

View file

@ -1,9 +1,9 @@
use super::common::{parse_table_name, parse_column_name, parse_condition};
use super::common::{parse_column_name, parse_condition, parse_table_name};
use crate::syntax::{ColumnSelection, RawQuerySyntax};
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{multispace0, multispace1, char},
character::complete::{char, multispace0, multispace1},
combinator::map,
error::Error,
multi::separated_list0,
@ -44,10 +44,12 @@ pub fn try_parse_column_selection(input: &str) -> IResult<&str, ColumnSelection>
#[cfg(test)]
mod tests {
use crate::parsing::{
common::{parse_column_name, parse_table_name},
select::parse_select,
};
use crate::syntax::{ColumnSelection, RawQuerySyntax};
use crate::parsing::{common::{parse_column_name, parse_table_name}, select::parse_select};
#[test]
fn test_parse_select_all() {
let sql = "SELECT * FROM \"MyTable\";";