hamelin_legacy 0.3.10

Legacy AST translation code for Hamelin (to be deprecated)
Documentation
use std::rc::Rc;

use hamelin_lib::{
    antlr::{
        completion_interval,
        hamelinparser::{
            ColumnReferenceContextAll, ColumnReferenceContextAttrs, ExpressionContextAll,
        },
        interval,
    },
    completion::Completion,
    err::{TranslationError, TranslationErrors},
    sql::expression::{
        identifier::{HamelinSimpleIdentifier, Identifier},
        literal::{ColumnReference, PatternVariableReference},
        SQLExpression,
    },
    translation::ExpressionTranslation,
};

use super::ExpressionTranslationContext;

pub struct HamelinColumnRef {
    pub parent_tree: Rc<ExpressionContextAll<'static>>,
    pub column_ref_tree: Option<Rc<ColumnReferenceContextAll<'static>>>,
    pub context: Rc<ExpressionTranslationContext>,
}

impl HamelinColumnRef {
    pub fn new(
        parent_tree: Rc<ExpressionContextAll<'static>>,
        column_ref_tree: Option<Rc<ColumnReferenceContextAll<'static>>>,
        context: Rc<ExpressionTranslationContext>,
    ) -> Self {
        HamelinColumnRef {
            parent_tree,
            column_ref_tree,
            context,
        }
    }

    pub fn translate(&self) -> Result<ExpressionTranslation, TranslationErrors> {
        let ctx = self.column_ref_tree.as_ref().ok_or(TranslationError::msg(
            self.parent_tree.as_ref(),
            "could not parse column reference",
        ))?;
        let sql_ident = HamelinSimpleIdentifier::new(ctx.simpleIdentifier().unwrap())
            .to_sql()?
            .into();
        let typ = self
            .context
            .bindings
            .lookup(&sql_ident)
            .map_err(|e| TranslationError::wrap(ctx.as_ref(), e).single())?;

        let sql: SQLExpression = match sql_ident {
            // Simple identifier column references can also be pattern variable references.
            // Look them up in the environment to see which one to slot into the SQL expression.
            // Pattern variable references are special, because they can't be quoted. We
            // always quote column references just to be safe in case there is an errant
            // character in the identifier.
            Identifier::Simple(si) => self
                .context
                .bindings
                .lookup_pattern_variable(&si)
                .map(|v| PatternVariableReference::new(v).into())
                .unwrap_or(ColumnReference::new(si.into()).into()),
            Identifier::Compound(ci) => ColumnReference::new(ci.into()).into(),
        };

        Ok(ExpressionTranslation::with_defaults(typ, sql))
    }

    pub fn append_completions(&self) {
        let maybe_guard = self.context.completions.try_borrow_mut();
        let range = completion_interval(self.parent_tree.as_ref());

        match (&self.column_ref_tree, self.context.at, maybe_guard) {
            (Some(cr), Some(at), Ok(mut guard)) if range.contains(&at) && guard.is_none() => {
                let insert_interval = interval(cr.as_ref());
                let mut completion = Completion::new(insert_interval);

                if *range.start() == at {
                    // We special-case when the cursor is on the first position of an identifier.
                    // This happens during "snippet completion" before the user has typed anything.
                    // So, when the cursor is at the beginning, drop the "open completion" set on it.
                    completion.filter(false);
                    completion.add_items(self.context.bindings.autocomplete_suggestions(false));
                    completion.add_items(self.context.registry.autocomplete_suggestions());
                    *guard = Some(completion);
                } else {
                    let maybe_curr_ident = cr
                        .simpleIdentifier()
                        .and_then(|si| HamelinSimpleIdentifier::new(si).to_sql().ok());

                    if let Some(curr_ident) = maybe_curr_ident {
                        completion.add_items(
                            self.context
                                .bindings
                                .nested_autocomplete_suggestions(&curr_ident, false),
                        );

                        completion.add_items(self.context.registry.autocomplete_suggestions());
                    }

                    *guard = Some(completion);
                }
            }
            _ => {}
        }
    }
}