beuvy 0.1.0

Facade crate for beuvy-runtime plus optional declarative UI authoring.
Documentation
#[path = "expression/helpers.rs"]
mod helpers;

use self::helpers::{
    evaluate_anchor_popup, evaluate_binary_expr, evaluate_math_args, resolve_runtime_path_in_scope,
};
use crate::ast::{DeclarativeRuntimeExpr, DeclarativeRuntimeStmt};
use crate::runtime::state::{
    DeclarativeLocalState, DeclarativeRefRects, DeclarativeRootComputedLocals,
    DeclarativeRootViewModel, DeclarativeUiRuntimeValues,
};
use crate::value::UiValue;
use bevy::prelude::*;
use std::collections::HashMap;

pub(crate) use self::helpers::truthy;

pub(crate) fn evaluate_runtime_expr(
    entity: Entity,
    expr: &DeclarativeRuntimeExpr,
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
    stack: &mut Vec<String>,
) -> Option<UiValue> {
    let mut locals = HashMap::new();
    evaluate_runtime_expr_with_locals(
        entity,
        expr,
        parents,
        states,
        computed,
        roots,
        values,
        ref_rects,
        stack,
        &mut locals,
    )
}

pub(super) fn evaluate_runtime_expr_with_locals(
    entity: Entity,
    expr: &DeclarativeRuntimeExpr,
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
    stack: &mut Vec<String>,
    locals: &mut HashMap<String, UiValue>,
) -> Option<UiValue> {
    match expr {
        DeclarativeRuntimeExpr::BindingPath(path) => resolve_runtime_path_in_scope(
            entity, path, parents, states, computed, roots, values, ref_rects, stack, locals,
        ),
        DeclarativeRuntimeExpr::Literal(value) => Some(UiValue::from_literal(value)),
        DeclarativeRuntimeExpr::NumberLiteral(number) => Some(UiValue::Number(*number)),
        DeclarativeRuntimeExpr::ArrayLiteral(items) => {
            Some(UiValue::list(items.iter().filter_map(|item| {
                evaluate_runtime_expr_with_locals(
                    entity, item, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                )
            })))
        }
        DeclarativeRuntimeExpr::GetBoundingClientRect { target_path } => {
            let ref_id = resolve_runtime_path_in_scope(
                entity,
                target_path,
                parents,
                states,
                computed,
                roots,
                values,
                ref_rects,
                stack,
                locals,
            )?
            .text()?
            .to_string();
            ref_rects.get_rect(&ref_id).cloned()
        }
        DeclarativeRuntimeExpr::FieldAccess { base, field } => evaluate_runtime_expr_with_locals(
            entity, base, parents, states, computed, roots, values, ref_rects, stack, locals,
        )?
        .field(field)
        .cloned(),
        DeclarativeRuntimeExpr::UnaryNot { expr } => {
            Some(UiValue::from(!truthy(&evaluate_runtime_expr_with_locals(
                entity, expr, parents, states, computed, roots, values, ref_rects, stack, locals,
            )?)))
        }
        DeclarativeRuntimeExpr::Binary { left, op, right } => {
            let left = evaluate_runtime_expr_with_locals(
                entity, left, parents, states, computed, roots, values, ref_rects, stack, locals,
            )?;
            let right = evaluate_runtime_expr_with_locals(
                entity, right, parents, states, computed, roots, values, ref_rects, stack, locals,
            )?;
            evaluate_binary_expr(left, *op, right)
        }
        DeclarativeRuntimeExpr::ObjectLiteral(fields) => {
            Some(UiValue::object(fields.iter().map(|(name, expr)| {
                let value = evaluate_runtime_expr_with_locals(
                    entity, expr, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                )
                .unwrap_or(UiValue::Null);
                (name.clone(), value)
            })))
        }
        DeclarativeRuntimeExpr::MathMin { args } => evaluate_math_args(
            entity, args, parents, states, computed, roots, values, ref_rects, stack, locals,
        )
        .map(|value: Vec<f64>| UiValue::from(value.into_iter().fold(f64::INFINITY, f64::min))),
        DeclarativeRuntimeExpr::MathMax { args } => evaluate_math_args(
            entity, args, parents, states, computed, roots, values, ref_rects, stack, locals,
        )
        .map(|value: Vec<f64>| UiValue::from(value.into_iter().fold(f64::NEG_INFINITY, f64::max))),
        DeclarativeRuntimeExpr::Conditional {
            condition,
            then_expr,
            else_expr,
        } => {
            if truthy(&evaluate_runtime_expr_with_locals(
                entity, condition, parents, states, computed, roots, values, ref_rects, stack,
                locals,
            )?) {
                evaluate_runtime_expr_with_locals(
                    entity, then_expr, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                )
            } else {
                evaluate_runtime_expr_with_locals(
                    entity, else_expr, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                )
            }
        }
        DeclarativeRuntimeExpr::Block(statements) => evaluate_runtime_block(
            entity, statements, parents, states, computed, roots, values, ref_rects, stack, locals,
        ),
        DeclarativeRuntimeExpr::AnchorPopup {
            anchor_rect,
            shell_rect,
            popup_width,
            popup_min_height,
            gap,
            margin,
        } => {
            let anchor_rect = evaluate_runtime_expr_with_locals(
                entity,
                anchor_rect,
                parents,
                states,
                computed,
                roots,
                values,
                ref_rects,
                stack,
                locals,
            )?;
            let shell_rect = evaluate_runtime_expr_with_locals(
                entity, shell_rect, parents, states, computed, roots, values, ref_rects, stack,
                locals,
            )?;
            let popup_width = evaluate_runtime_expr_with_locals(
                entity,
                popup_width,
                parents,
                states,
                computed,
                roots,
                values,
                ref_rects,
                stack,
                locals,
            )?
            .number()? as f32;
            let popup_min_height = evaluate_runtime_expr_with_locals(
                entity,
                popup_min_height,
                parents,
                states,
                computed,
                roots,
                values,
                ref_rects,
                stack,
                locals,
            )?
            .number()? as f32;
            let gap = evaluate_runtime_expr_with_locals(
                entity, gap, parents, states, computed, roots, values, ref_rects, stack, locals,
            )?
            .number()? as f32;
            let margin = evaluate_runtime_expr_with_locals(
                entity, margin, parents, states, computed, roots, values, ref_rects, stack, locals,
            )?
            .number()? as f32;
            evaluate_anchor_popup(
                &anchor_rect,
                &shell_rect,
                popup_width,
                popup_min_height,
                gap,
                margin,
            )
        }
    }
}

fn evaluate_runtime_block(
    entity: Entity,
    statements: &[DeclarativeRuntimeStmt],
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
    stack: &mut Vec<String>,
    locals: &mut HashMap<String, UiValue>,
) -> Option<UiValue> {
    let mut block_locals = locals.clone();
    evaluate_runtime_statements(
        entity,
        statements,
        parents,
        states,
        computed,
        roots,
        values,
        ref_rects,
        stack,
        &mut block_locals,
    )
}

fn evaluate_runtime_statements(
    entity: Entity,
    statements: &[DeclarativeRuntimeStmt],
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
    stack: &mut Vec<String>,
    locals: &mut HashMap<String, UiValue>,
) -> Option<UiValue> {
    for statement in statements {
        match statement {
            DeclarativeRuntimeStmt::Const { name, expr } => {
                let value = evaluate_runtime_expr_with_locals(
                    entity, expr, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                )?;
                locals.insert(name.clone(), value);
            }
            DeclarativeRuntimeStmt::If {
                condition,
                then_branch,
                else_branch,
            } => {
                let branch = if truthy(&evaluate_runtime_expr_with_locals(
                    entity, condition, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                )?) {
                    then_branch
                } else {
                    else_branch
                };
                if let Some(value) = evaluate_runtime_statements(
                    entity, branch, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                ) {
                    return Some(value);
                }
            }
            DeclarativeRuntimeStmt::Return(expr) => {
                return evaluate_runtime_expr_with_locals(
                    entity, expr, parents, states, computed, roots, values, ref_rects, stack,
                    locals,
                );
            }
        }
    }
    None
}