neuxdb 0.1.0

A super simple, embedded, encrypted database like SQLite, using pipe-separated files and age encryption.
Documentation
use super::parse_create::parse_create;
use super::parse_delete::parse_delete;
use super::parse_insert::parse_insert;
use super::parse_select::parse_select;
use super::parse_update::parse_update;
use super::tokenizer::tokenize;
use crate::core::syntax::{ComparisonOp, Statement, WhereClause};
use crate::error::NeuxDbError;
use crate::types::Value;
use std::iter::Peekable;
use std::slice::Iter;
pub fn parse(sql: &str) -> Result<Statement, NeuxDbError> {
    let tokens = tokenize(sql);
    let mut iter = tokens.iter().peekable();
    let command = iter.peek().map(|s| s.to_lowercase());
    if command.as_deref() == Some("create") {
        iter.next();
        parse_create(&mut iter)
    } else if command.as_deref() == Some("drop") {
        iter.next();
        parse_drop(&mut iter)
    } else if command.as_deref() == Some("show") {
        iter.next();
        parse_show(&mut iter)
    } else if command.as_deref() == Some("insert") {
        iter.next();
        parse_insert(&mut iter)
    } else if command.as_deref() == Some("select") {
        iter.next();
        parse_select(&mut iter)
    } else if command.as_deref() == Some("update") {
        iter.next();
        parse_update(&mut iter)
    } else if command.as_deref() == Some("delete") {
        iter.next();
        parse_delete(&mut iter)
    } else if let Some(other) = command {
        Err(NeuxDbError::Parse(format!(
            "Unknown command '{}'. Expected CREATE, DROP, SHOW, INSERT, SELECT, UPDATE, or DELETE",
            other
        )))
    } else {
        Err(NeuxDbError::Parse("Empty query".into()))
    }
}
fn parse_drop(iter: &mut Peekable<Iter<String>>) -> Result<Statement, NeuxDbError> {
    match iter.next() {
        Some(t) if t.to_lowercase() == "table" => {}
        _ => return Err(NeuxDbError::Parse("Expected 'TABLE' after DROP".into())),
    }
    let name = next_identifier(iter, "table name")?;
    Ok(Statement::DropTable { name })
}
fn parse_show(iter: &mut Peekable<Iter<String>>) -> Result<Statement, NeuxDbError> {
    match iter.next() {
        Some(t) if t.to_lowercase() == "tables" => {}
        _ => return Err(NeuxDbError::Parse("Expected 'TABLES' after SHOW".into())),
    }
    Ok(Statement::ShowTables)
}
pub(super) fn next_identifier<'a>(
    iter: &mut Peekable<Iter<'a, String>>,
    expected: &str,
) -> Result<String, NeuxDbError> {
    match iter.next() {
        Some(t) => Ok(t.clone()),
        None => Err(NeuxDbError::Parse(format!("Missing {}", expected))),
    }
}
pub(super) fn parse_where_clause(
    iter: &mut Peekable<Iter<String>>,
) -> Result<WhereClause, NeuxDbError> {
    let left = parse_condition(iter)?;
    let next = iter.peek().map(|s| s.to_lowercase());
    if next.as_deref() == Some("and") {
        iter.next();
        let right = parse_where_clause(iter)?;
        Ok(WhereClause::And(Box::new(left), Box::new(right)))
    } else if next.as_deref() == Some("or") {
        iter.next();
        let right = parse_where_clause(iter)?;
        Ok(WhereClause::Or(Box::new(left), Box::new(right)))
    } else {
        Ok(left)
    }
}
fn parse_condition(iter: &mut Peekable<Iter<String>>) -> Result<WhereClause, NeuxDbError> {
    let column = next_identifier(iter, "column name")?;
    let op = match iter.next() {
        Some(op) => op.clone(),
        None => return Err(NeuxDbError::Parse("Missing operator after column".into())),
    };
    let operator = match op.as_str() {
        "=" => ComparisonOp::Eq,
        "!=" | "<>" => ComparisonOp::Ne,
        "<" => ComparisonOp::Lt,
        ">" => ComparisonOp::Gt,
        "<=" => ComparisonOp::Le,
        ">=" => ComparisonOp::Ge,
        s if s.to_lowercase() == "like" => ComparisonOp::Like,
        _ => return Err(NeuxDbError::Parse(format!("Unknown operator '{}'", op))),
    };
    let raw_val = next_identifier(iter, "value")?;
    let unquoted = super::unquote::unquote(&raw_val);
    let value = Value::from(unquoted.as_str());
    Ok(WhereClause::Condition {
        column,
        operator,
        value,
    })
}