use std::str::FromStr;
extern crate pest;
extern crate thiserror;
#[macro_use]
extern crate pest_derive;
use pest::Parser;
use thiserror::Error;
#[derive(Parser)]
#[grammar = "sql_grammar.pest"]
pub struct SQLParser;
#[derive(Debug, Error)]
pub enum SqlParseError {
#[error("Invalid data")]
InvalidData,
}
#[derive(Debug)]
pub enum SqlType {
Int,
Text,
Bool,
}
impl FromStr for SqlType {
type Err = SqlParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"INT" => Ok(SqlType::Int),
"TEXT" => Ok(SqlType::Text),
"BOOL" => Ok(SqlType::Bool),
_ => Err(SqlParseError::InvalidData),
}
}
}
impl PartialEq for SqlType {
fn eq(&self, other: &SqlType) -> bool {
match (&self, other) {
(SqlType::Int, SqlType::Int) => true,
(SqlType::Text, SqlType::Text) => true,
(SqlType::Bool, SqlType::Bool) => true,
(_, _) => false,
}
}
}
#[derive(Debug)]
pub struct ColumnInfo {
pub column_name: String,
pub column_type: SqlType,
}
#[derive(Debug)]
pub struct ColumnInfoOption {
pub column_name: Option<String>,
pub column_type: Option<SqlType>,
}
#[derive(Debug)]
pub struct CreateTable {
pub table_name: String,
pub column_info: Vec<ColumnInfo>,
}
#[derive(Debug)]
pub struct CreateTableOption {
pub table_name: Option<String>,
pub column_info: Vec<Option<ColumnInfoOption>>,
}
#[derive(Debug)]
pub enum Parsed {
CreateTable(CreateTable),
}
pub fn unwrap_column_info(column_info: &mut Vec<Option<ColumnInfoOption>>) -> Vec<ColumnInfo> {
if column_info.is_empty() {
return Vec::<ColumnInfo>::new();
}
let cdef = column_info.pop().unwrap().unwrap();
let cd = ColumnInfo {
column_name: cdef.column_name.unwrap(),
column_type: cdef.column_type.unwrap(),
};
let mut result = unwrap_column_info(column_info);
result.push(cd);
result
}
pub fn parse_create_table(pairs: pest::iterators::FlatPairs<'_, Rule>) -> CreateTable {
let mut table_names = CreateTableOption {
table_name: None,
column_info: Vec::new(),
};
let mut column_name: Option<String> = None;
for child in pairs {
match child.as_rule() {
Rule::table_name => {
let table_name = child.as_str();
table_names.table_name = Some(String::from(table_name))
}
Rule::column_name => {
column_name = Some(String::from(child.as_str()));
}
Rule::column_type => {
table_names.column_info.push(Some(ColumnInfoOption {
column_name: column_name.clone(),
column_type: Some(SqlType::from_str(child.as_str()).unwrap()),
}));
}
_ => (),
}
}
let column_info = unwrap_column_info(&mut table_names.column_info);
CreateTable {
table_name: table_names.table_name.unwrap(),
column_info,
}
}
pub fn parse_sql(query: &str) -> Result<Parsed, SqlParseError> {
let parsed = SQLParser::parse(Rule::sql_grammar, query)
.map_err(|_e| SqlParseError::InvalidData)?
.next()
.ok_or(SqlParseError::InvalidData)?;
let pairs = parsed.into_inner();
if let Some(_child) = pairs.clone().flatten().next() {
let create_table = parse_create_table(pairs.clone().flatten());
return Ok(Parsed::CreateTable(create_table));
}
Err(SqlParseError::InvalidData)
}