fluidattacks-blends-domain 0.17.1

Blends functional core: pure AST graph to syntax graph (no_std)
Documentation
use crate::syntax::fields::ruby::call;
use crate::{
    query::{get_nodes_by_path, match_ast_group_d},
    syntax::{
        builders::{
            import_statement::{build_import_statement_node, ImportElement},
            method_invocation::{build_method_invocation_node, DirectChildren},
        },
        SyntaxGraphArgs, SyntaxGraphError,
    },
    utilities::text_nodes::node_to_str,
    NodeId,
};
use alloc::borrow::ToOwned;
use alloc::{collections::BTreeSet, string::String};
use alloc::{format, vec};

fn process_ruby_imports(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    argument_id: NodeId,
    method_name: &str,
) -> Result<Option<NodeId>, SyntaxGraphError> {
    let graph = args.ast_graph;
    let mut module_nodes = BTreeSet::<NodeId>::new();
    let first_path = get_nodes_by_path(graph, argument_id, &["string", "string_content"]);
    let second_path = get_nodes_by_path(
        graph,
        argument_id,
        &["string", "interpolation", "identifier"],
    );

    let arg = match_ast_group_d(graph, argument_id, "identifier", Some(1));

    module_nodes.extend(first_path);
    module_nodes.extend(second_path);
    module_nodes.extend(arg);

    let mut import_attrs: vec::Vec<ImportElement> = vec::Vec::new();
    for module_id in module_nodes {
        let module_expression = node_to_str(graph, module_id);
        import_attrs.push(ImportElement {
            expression: Some(module_expression),
            method_name: Some(method_name.to_owned()),
            corrected_n_id: Some(module_id),
            ..Default::default()
        });
    }

    build_import_statement_node(args, n_id, &import_attrs, false, &[]).map(Some)
}

pub fn reader(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
) -> Result<Option<NodeId>, SyntaxGraphError> {
    let graph = args.ast_graph;

    let argument_id = args.optional_field_alt(n_id, call::ARGUMENTS);
    let method_id = args.optional_field_alt(n_id, call::METHOD);
    let operator_id = args.optional_field_alt(n_id, call::OPERATOR);
    let object_id = args.optional_field_alt(n_id, call::RECEIVER);
    let block_id = args.optional_field_alt(n_id, call::BLOCK);

    let mut method_name = method_id.map_or_else(String::new, |id| node_to_str(graph, id));

    if operator_id.is_some_and(|op_id| node_to_str(graph, op_id) == "::") {
        method_name = format!("::{method_name}");
    }

    let ruby_imports = &["require", "require_relative", "load", "autoload"];

    if ruby_imports.contains(&method_name.as_str()) && object_id.is_none() {
        if let Some(arguments_id) = argument_id {
            return process_ruby_imports(args, n_id, arguments_id, &method_name);
        }
    }

    let children = DirectChildren {
        expression_id: method_id,
        arguments_id: argument_id,
        object_id,
        block_id,
    };

    build_method_invocation_node(args, n_id, method_name, children, None).map(Some)
}