squawk-ide 2.63.0

Linter for Postgres migrations & SQL
Documentation
use rowan::TextSize;
use salsa::Database as Db;
use squawk_linter::Edit;
use squawk_syntax::ast::{self, AstNode};

use crate::{file::InFile, offsets::token_from_offset};

use super::{ActionKind, CodeAction};

pub(super) fn rewrite_xmlexists_as_function_call(
    db: &dyn Db,
    position: InFile<TextSize>,
    actions: &mut Vec<CodeAction>,
) -> Option<()> {
    let token = token_from_offset(db, position)?;
    let xml_exists = token.parent_ancestors().find_map(ast::XmlExistsFn::cast)?;
    let passing = xml_exists.xml_row_passing_clause()?;
    let xpath = passing.row()?;
    let xml = passing.xml_passing_doc()?.expr()?;

    actions.push(CodeAction {
        title: "Rewrite as function call `pg_catalog.xmlexists()`".to_owned(),
        edits: vec![Edit::replace(
            xml_exists.syntax().text_range(),
            format!(
                "pg_catalog.xmlexists({}, {})",
                xpath.syntax().text(),
                xml.syntax().text()
            ),
        )],
        kind: ActionKind::RefactorRewrite,
    });

    Some(())
}

#[cfg(test)]
mod test {
    use insta::assert_snapshot;

    use crate::code_actions::test_utils::{apply_code_action, code_action_not_applicable};

    use super::rewrite_xmlexists_as_function_call;

    #[test]
    fn rewrites_xmlexists_as_function_call() {
        assert_snapshot!(
            apply_code_action(
                rewrite_xmlexists_as_function_call,
                "select XML$0EXISTS('//x' PASSING XMLPARSE(DOCUMENT '<r><x/></r>'));",
            ),
            @"select pg_catalog.xmlexists('//x', XMLPARSE(DOCUMENT '<r><x/></r>'));"
        );
    }

    #[test]
    fn applies_with_cursor_in_passing_expression() {
        assert_snapshot!(
            apply_code_action(
                rewrite_xmlexists_as_function_call,
                "select xmlexists(xpath passing xmlparse(document xm$0l_source));",
            ),
            @"select pg_catalog.xmlexists(xpath, xmlparse(document xml_source));"
        );
    }

    #[test]
    fn omits_optional_passing_mechanisms() {
        assert_snapshot!(
            apply_code_action(
                rewrite_xmlexists_as_function_call,
                "select xmlexists(path passing by va$0lue document by ref);",
            ),
            @"select pg_catalog.xmlexists(path, document);"
        );
    }

    #[test]
    fn rewrites_innermost_call_and_preserves_aggregate_clause() {
        assert_snapshot!(
            apply_code_action(
                rewrite_xmlexists_as_function_call,
                "select xmlexists(path passing xmlexists(inner_path pass$0ing document)::xml) FILTER (WHERE ok);",
            ),
            @"select xmlexists(path passing pg_catalog.xmlexists(inner_path, document)::xml) FILTER (WHERE ok);"
        );
    }

    #[test]
    fn not_applicable_outside_xmlexists() {
        assert!(code_action_not_applicable(
            rewrite_xmlexists_as_function_call,
            "select xmlparse(document xm$0l_source);"
        ));
    }
}