hamelin_legacy 0.4.3

Legacy AST translation code for Hamelin (to be deprecated)
Documentation
use derive_more::derive::From;
use hamelin_lib::completion::Completion;
use serde::Serialize;
use std::cell::RefCell;
use std::fmt::Debug;
use std::fmt::{Display, Formatter};
use std::rc::Rc;
use tsify_next::Tsify;

use hamelin_lib::antlr::fit;
use hamelin_lib::catalog::Column;
use hamelin_lib::err::{Context, ContextualTranslationError, TranslationError, TranslationErrors};
use hamelin_lib::sql::query::dml;
use hamelin_lib::sql::query::SQLQuery;
use hamelin_lib::sql::statement::Statement;

use crate::env::Environment;

pub mod projection_builder;
pub mod sql_query_helpers;

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PendingQuery {
    pub query: SQLQuery,
    pub env: Environment,
}

impl PendingQuery {
    pub fn new(query: SQLQuery, env: Environment) -> Self {
        Self { query, env }
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PendingQueryResult {
    pub translation: PendingQuery,
    pub errors: TranslationErrors,
}

impl PendingQueryResult {
    pub fn consume_result(&mut self, result: Result<PendingQuery, TranslationErrors>) {
        match result {
            Ok(pending_query) => {
                self.translation = pending_query;
            }
            Err(errors) => {
                self.errors.extend(errors);
            }
        }
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PendingStatement {
    pub statement: Statement,
    pub env: Environment,
}

impl PendingStatement {
    pub fn new(statement: Statement, env: Environment) -> Self {
        Self { statement, env }
    }

    pub fn new_query(query: SQLQuery, env: Environment) -> Self {
        Self {
            statement: Statement::SQLQuery(query),
            env,
        }
    }

    pub fn new_dml(dml: dml::DML, env: Environment) -> Self {
        Self {
            statement: Statement::DML(dml),
            env,
        }
    }
}

impl From<PendingQuery> for PendingStatement {
    fn from(pending_query: PendingQuery) -> Self {
        Self {
            statement: Statement::SQLQuery(pending_query.query),
            env: pending_query.env,
        }
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PendingStatementResult {
    pub translation: PendingStatement,
    pub errors: TranslationErrors,
}

impl PendingStatementResult {
    pub fn with_translation(mut self, translation: PendingStatement) -> Self {
        self.translation = translation;
        self
    }

    pub fn with_errors(mut self, errors: TranslationErrors) -> Self {
        self.errors = errors;
        self
    }

    pub fn with_error(mut self, error: TranslationError) -> Self {
        self.errors.add(error);
        self
    }

    pub fn add_error(&mut self, error: TranslationError) {
        self.errors.add(error);
    }

    pub fn add_errors(&mut self, errors: TranslationErrors) {
        self.errors.extend(errors);
    }

    pub fn recover(&mut self, other: PendingStatementResult) -> PendingStatement {
        self.errors.extend(other.errors);
        other.translation
    }

    pub fn into_result(self) -> Result<PendingStatement, TranslationErrors> {
        if self.errors.is_empty() {
            Ok(self.translation)
        } else {
            Err(self.errors)
        }
    }
}

#[derive(From, Debug, Clone, PartialEq, Eq, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub enum StatementTranslation {
    Query(QueryTranslation),
    DML(DMLTranslation),
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub struct QueryTranslation {
    pub translation: Translation,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub struct DMLTranslation {
    pub translation: Translation,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub struct Translation {
    pub sql: String,
    pub columns: Vec<Column>,
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, Tsify)]
#[tsify(into_wasm_abi)]
pub struct ContextualCompletion {
    pub pretty: String,
    pub completion: Completion,
}

impl ContextualCompletion {
    fn from_completion(mut value: Completion, hamelin: &String) -> Self {
        value.at = fit(&value.at, &hamelin);

        Self {
            pretty: value.make_pretty(hamelin).unwrap_or_default(),
            completion: value,
        }
    }
}

impl Display for ContextualCompletion {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.pretty)
    }
}

#[derive(Debug, Clone, Serialize, PartialEq, Eq, Tsify)]
#[tsify(into_wasm_abi)]
pub struct ContextualResult {
    pub hamelin: String,
    pub errors: Vec<ContextualTranslationError>,
    pub completions: Option<ContextualCompletion>,
    pub translation: Option<QueryTranslation>,
}

impl ContextualResult {
    pub fn new(hamelin: String) -> Self {
        Self {
            hamelin,
            errors: Vec::new(),
            completions: None,
            translation: None,
        }
    }

    pub fn with_errors(mut self, errors: TranslationErrors) -> Self {
        self.add_errors(errors);
        self
    }

    pub fn add_errors(&mut self, errors: TranslationErrors) {
        for error in errors.0 {
            self.add_error(error);
        }
    }

    pub fn add_error(&mut self, mut e: TranslationError) {
        e.primary.interval = fit(&e.primary.interval, &self.hamelin);
        if let Some(supporting) = &mut e.supporting {
            for c in supporting.iter_mut() {
                c.interval = fit(&c.interval, &self.hamelin);
            }
        }
        let pretty = e.make_pretty(&self.hamelin).unwrap_or_default();
        self.errors.push(ContextualTranslationError::new(e, pretty));
    }

    pub fn with_error(mut self, error: TranslationError) -> Self {
        self.add_error(error);
        self
    }

    pub fn with_pending_result(mut self, pending_result: PendingStatementResult) -> Self {
        if pending_result.errors.is_empty() {
            let cols = pending_result.translation.env.into_external_columns();
            match pending_result.translation.statement {
                Statement::SQLQuery(sqlquery) => {
                    self.translation = Some(QueryTranslation {
                        translation: Translation {
                            sql: sqlquery.to_string(),
                            columns: cols,
                        },
                    });
                }
                Statement::DML(_) => {
                    self.add_error(TranslationError::new(Context::new(
                        0..=self.hamelin.len() - 1,
                        "statement has side effects",
                    )));
                    self.translation = None;
                }
            }
        } else {
            self.translation = None;
        }

        self = self.with_errors(pending_result.errors);
        self
    }

    pub fn with_completions(mut self, completions: Rc<RefCell<Option<Completion>>>) -> Self {
        let guard = completions.borrow();
        if let Some(ref c) = *guard {
            self.completions = Some(ContextualCompletion::from_completion(
                c.clone(),
                &self.hamelin,
            ));
        }

        self
    }
}