fluidattacks-blends-domain 0.30.0

Blends functional core: pure AST graph to syntax graph (no_std)
Documentation
use crate::ast::AstGraph;
use crate::query::match_ast_group_d;
use crate::syntax::builders::array_node::build_array_node;
use crate::syntax::builders::object::build_object_node;
use crate::syntax::builders::object_creation::build_object_creation_node;
use crate::syntax::fields::go::composite_literal;
use crate::syntax::readers::go::common::{literal_elements, ARRAY_TYPES};
use crate::syntax::{SyntaxGraphArgs, SyntaxGraphError};
use crate::utilities::text_nodes::node_to_str;
use crate::{CodeGraph, NodeId};

const NAMED_TYPES: &[&str] = &["qualified_type", "type_identifier"];

/// The type each element of `type_id` takes when it omits its own, `Point` for
/// a `[]Point`. Only a list gives its elements a type.
fn element_type_id(graph: &AstGraph, type_id: NodeId) -> Option<NodeId> {
    if !ARRAY_TYPES.contains(&graph.label_type(type_id).unwrap_or_default()) {
        return None;
    }
    graph.nodes.get(&type_id)?.fields.get("element_id").copied()
}

/// Build at `n_id` the list, the record or the table that `type_id` names,
/// holding what `body_id` lists. An element that omits its type builds the same
/// way, with the type its enclosing literal gives it.
pub fn build_composite(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    type_id: NodeId,
    body_id: NodeId,
) -> Result<NodeId, SyntaxGraphError> {
    // Every shape pushes, a record and a table pushing no type at all, so an
    // element reads the type of the literal holding it and never one from
    // further out.
    let element_id = element_type_id(args.ast_graph, type_id);
    args.metadata.element_type_stack.push(element_id);
    let built = build_shape(args, n_id, type_id, body_id);
    args.metadata.element_type_stack.pop();
    built
}

fn build_shape(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
    type_id: NodeId,
    body_id: NodeId,
) -> Result<NodeId, SyntaxGraphError> {
    let graph = args.ast_graph;
    let type_kind = graph.label_type(type_id).unwrap_or_default();
    let type_name = node_to_str(graph, type_id);

    if ARRAY_TYPES.contains(&type_kind) {
        let elements = literal_elements(graph, body_id);
        // The list names what it holds when that has a name, `Point` for a
        // `[]Point`. A list of lists, of pointers or of unnamed records names
        // nothing: each element already carries what it is.
        let element_type = element_type_id(graph, type_id)
            .filter(|id| NAMED_TYPES.contains(&graph.label_type(*id).unwrap_or_default()))
            .map(|id| node_to_str(graph, id));
        return build_array_node(args, n_id, &elements, element_type);
    }

    if !match_ast_group_d(graph, body_id, "keyed_element", None).is_empty() {
        // A map names no type to initialize, its keys are values of its own.
        let initializer_id = (type_kind != "map_type").then_some(type_id);
        return build_object_creation_node(
            args,
            n_id,
            type_name,
            Some(body_id),
            initializer_id,
            None,
        );
    }

    let name = NAMED_TYPES.contains(&type_kind).then_some(type_name);
    // A qualified type reaches the graph as the member access it is, so the
    // package it names stays a usage of the import.
    let c_ids: &[NodeId] = if type_kind == "qualified_type" {
        &[type_id, body_id]
    } else {
        &[body_id]
    };
    build_object_node(args, n_id, c_ids, name, None)
}

pub fn reader(
    args: &mut SyntaxGraphArgs<'_>,
    n_id: NodeId,
) -> Result<Option<NodeId>, SyntaxGraphError> {
    let type_id = args.required_field_alt(n_id, composite_literal::TYPE)?;
    let body_id = args.required_field_alt(n_id, composite_literal::BODY)?;
    build_composite(args, n_id, type_id, body_id).map(Some)
}