solverforge-macros 0.15.0

Derive macros for SolverForge constraint solver
Documentation
use super::ast::{
    ConstraintFunction, ConstraintProgram, SharedGroupedProgram, StreamNode, TailMember,
};

pub(crate) fn plan(function: ConstraintFunction) -> ConstraintProgram {
    let bindings = repeated_grouped_bindings(&function);
    if bindings.is_empty() {
        return ConstraintProgram::Passthrough(function);
    }

    ConstraintProgram::SharedGrouped(SharedGroupedProgram {
        item: function.item,
        prefix_statements: function.prefix_statements,
        tail_members: function.tail_members,
        bindings,
    })
}

fn repeated_grouped_bindings(function: &ConstraintFunction) -> Vec<String> {
    let mut bindings = Vec::new();
    for member in &function.tail_members {
        let TailMember::Terminal(candidate) = member else {
            continue;
        };
        let binding = &candidate.source_binding;
        if bindings.iter().any(|existing| existing == binding) {
            continue;
        }
        let Some(node) = node_by_binding(&function.stream_nodes, binding) else {
            continue;
        };
        if !node.supports_grouped_sharing {
            continue;
        }
        let count = function
            .tail_members
            .iter()
            .filter(|member| {
                matches!(member, TailMember::Terminal(terminal) if &terminal.source_binding == binding)
            })
            .count();
        if count >= 2 {
            bindings.push(binding.to_string());
        }
    }
    bindings
}

fn node_by_binding<'a>(nodes: &'a [StreamNode], binding: &str) -> Option<&'a StreamNode> {
    nodes.iter().rev().find(|node| node.binding == binding)
}