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
}
}