omena-bridge 0.2.0

CME-coupled bridge crate for Omena semantic graph inputs
Documentation
use oxc_ast::ast::{
    Argument, ArrayExpression, CallExpression, ExportDefaultDeclarationKind, Expression,
    ObjectExpression, Program, Statement,
};

use super::syntax::{binding_pattern_identifier_name, expression_identifier_name};

pub(super) fn top_level_object_literals<'a>(
    program: &'a Program<'a>,
) -> Vec<(String, &'a ObjectExpression<'a>)> {
    let mut literals = Vec::new();
    for statement in &program.body {
        let Statement::VariableDeclaration(declaration) = statement else {
            continue;
        };
        for declarator in &declaration.declarations {
            let Some(binding) = binding_pattern_identifier_name(&declarator.id) else {
                continue;
            };
            let Some(init) = declarator.init.as_ref() else {
                continue;
            };
            if let Some(object) = unwrap_config_expression(init, literals.as_slice()) {
                upsert_top_level_literal(&mut literals, binding.to_string(), object);
            }
        }
    }
    literals
}

pub(super) fn top_level_array_literals<'a>(
    program: &'a Program<'a>,
) -> Vec<(String, &'a ArrayExpression<'a>)> {
    let mut literals = Vec::new();
    for statement in &program.body {
        let Statement::VariableDeclaration(declaration) = statement else {
            continue;
        };
        for declarator in &declaration.declarations {
            let Some(binding) = binding_pattern_identifier_name(&declarator.id) else {
                continue;
            };
            let Some(init) = declarator.init.as_ref() else {
                continue;
            };
            if let Some(array) = unwrap_array_expression(init, literals.as_slice()) {
                upsert_top_level_array_literal(&mut literals, binding.to_string(), array);
            }
        }
    }
    literals
}

pub(super) fn unwrap_export_default_declaration_kind<'a>(
    declaration: &'a ExportDefaultDeclarationKind<'a>,
    top_level_literals: &[(String, &'a ObjectExpression<'a>)],
) -> Option<&'a ObjectExpression<'a>> {
    match declaration {
        ExportDefaultDeclarationKind::ObjectExpression(object) => Some(object),
        ExportDefaultDeclarationKind::Identifier(identifier) => {
            find_top_level_literal(top_level_literals, identifier.name.as_str())
        }
        ExportDefaultDeclarationKind::ParenthesizedExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        ExportDefaultDeclarationKind::TSAsExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        ExportDefaultDeclarationKind::TSSatisfiesExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        ExportDefaultDeclarationKind::CallExpression(expression) => {
            unwrap_config_call_expression(expression, top_level_literals)
        }
        _ => None,
    }
}

pub(super) fn unwrap_config_expression<'a>(
    expression: &'a Expression<'a>,
    top_level_literals: &[(String, &'a ObjectExpression<'a>)],
) -> Option<&'a ObjectExpression<'a>> {
    match expression {
        Expression::ObjectExpression(object) => Some(object),
        Expression::Identifier(identifier) => {
            find_top_level_literal(top_level_literals, identifier.name.as_str())
        }
        Expression::ParenthesizedExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        Expression::TSAsExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        Expression::TSSatisfiesExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        Expression::CallExpression(expression) => {
            unwrap_config_call_expression(expression, top_level_literals)
        }
        _ => None,
    }
}

pub(super) fn unwrap_static_object_expression<'a>(
    expression: &'a Expression<'a>,
) -> Option<&'a ObjectExpression<'a>> {
    match expression {
        Expression::ObjectExpression(object) => Some(object),
        Expression::ParenthesizedExpression(expression) => {
            unwrap_static_object_expression(&expression.expression)
        }
        Expression::TSAsExpression(expression) => {
            unwrap_static_object_expression(&expression.expression)
        }
        Expression::TSSatisfiesExpression(expression) => {
            unwrap_static_object_expression(&expression.expression)
        }
        _ => None,
    }
}

pub(super) fn find_top_level_literal<'a>(
    literals: &[(String, &'a ObjectExpression<'a>)],
    name: &str,
) -> Option<&'a ObjectExpression<'a>> {
    literals
        .iter()
        .find_map(|(literal_name, object)| (literal_name == name).then_some(*object))
}

pub(super) fn find_top_level_array_literal<'a>(
    literals: &[(String, &'a ArrayExpression<'a>)],
    name: &str,
) -> Option<&'a ArrayExpression<'a>> {
    literals
        .iter()
        .find_map(|(literal_name, array)| (literal_name == name).then_some(*array))
}

fn unwrap_config_call_expression<'a>(
    expression: &'a CallExpression<'a>,
    top_level_literals: &[(String, &'a ObjectExpression<'a>)],
) -> Option<&'a ObjectExpression<'a>> {
    if expression_identifier_name(&expression.callee) != Some("defineConfig") {
        return None;
    }
    expression
        .arguments
        .first()
        .and_then(|argument| unwrap_config_argument(argument, top_level_literals))
}

fn unwrap_config_argument<'a>(
    argument: &'a Argument<'a>,
    top_level_literals: &[(String, &'a ObjectExpression<'a>)],
) -> Option<&'a ObjectExpression<'a>> {
    match argument {
        Argument::ObjectExpression(object) => Some(object),
        Argument::Identifier(identifier) => {
            find_top_level_literal(top_level_literals, identifier.name.as_str())
        }
        Argument::ParenthesizedExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        Argument::TSAsExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        Argument::TSSatisfiesExpression(expression) => {
            unwrap_config_expression(&expression.expression, top_level_literals)
        }
        Argument::CallExpression(expression) => {
            unwrap_config_call_expression(expression, top_level_literals)
        }
        _ => None,
    }
}

fn unwrap_array_expression<'a>(
    expression: &'a Expression<'a>,
    top_level_arrays: &[(String, &'a ArrayExpression<'a>)],
) -> Option<&'a ArrayExpression<'a>> {
    match expression {
        Expression::ArrayExpression(array) => Some(array),
        Expression::Identifier(identifier) => {
            find_top_level_array_literal(top_level_arrays, identifier.name.as_str())
        }
        Expression::ParenthesizedExpression(expression) => {
            unwrap_array_expression(&expression.expression, top_level_arrays)
        }
        Expression::TSAsExpression(expression) => {
            unwrap_array_expression(&expression.expression, top_level_arrays)
        }
        Expression::TSSatisfiesExpression(expression) => {
            unwrap_array_expression(&expression.expression, top_level_arrays)
        }
        _ => None,
    }
}

fn upsert_top_level_literal<'a>(
    literals: &mut Vec<(String, &'a ObjectExpression<'a>)>,
    name: String,
    object: &'a ObjectExpression<'a>,
) {
    if let Some(index) = literals
        .iter()
        .position(|(literal_name, _)| literal_name == &name)
    {
        literals.remove(index);
    }
    literals.push((name, object));
}

fn upsert_top_level_array_literal<'a>(
    literals: &mut Vec<(String, &'a ArrayExpression<'a>)>,
    name: String,
    array: &'a ArrayExpression<'a>,
) {
    if let Some(index) = literals
        .iter()
        .position(|(literal_name, _)| literal_name == &name)
    {
        literals.remove(index);
    }
    literals.push((name, array));
}