beuvy 0.1.0

Facade crate for beuvy-runtime plus optional declarative UI authoring.
Documentation
use super::expression::evaluate_runtime_expr;
use crate::ast::DeclarativeConditionExpr;
use crate::runtime::context::resolve_path;
use crate::runtime::state::{
    DeclarativeLocalState, DeclarativeRefRects, DeclarativeRootComputedLocals,
    DeclarativeRootViewModel, DeclarativeUiRuntimeValues,
};
use crate::value::UiValue;
use bevy::prelude::*;

pub(super) fn resolve_runtime_condition(
    entity: Entity,
    expr: &DeclarativeConditionExpr,
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
) -> bool {
    match expr {
        DeclarativeConditionExpr::Binding(path) => resolve_runtime_path(
            entity, path, parents, states, computed, roots, values, ref_rects,
        )
        .and_then(|value| value.bool())
        .unwrap_or(false),
        DeclarativeConditionExpr::Equals { name, value } => resolve_runtime_path(
            entity, name, parents, states, computed, roots, values, ref_rects,
        )
        .is_some_and(|candidate| candidate == UiValue::from_literal(value)),
    }
}

pub fn resolve_runtime_path(
    entity: Entity,
    path: &str,
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
) -> Option<UiValue> {
    let mut stack = Vec::new();
    resolve_runtime_path_with_extra_locals(
        entity, path, None, parents, states, computed, roots, values, ref_rects, &mut stack,
    )
}

pub(crate) fn resolve_runtime_path_with_extra_locals(
    entity: Entity,
    path: &str,
    extra_locals: Option<&std::collections::HashMap<String, UiValue>>,
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
    stack: &mut Vec<String>,
) -> Option<UiValue> {
    if path == "props" {
        let mut current = Some(entity);
        while let Some(candidate) = current {
            if let Ok(root) = roots.get(candidate) {
                return Some(root.0.clone());
            }
            current = parents.get(candidate).ok().map(ChildOf::parent);
        }
        return None;
    }
    let path = path.strip_prefix("props.").unwrap_or(path);
    if let Some(extra_locals) = extra_locals {
        if let Some(value) = extra_locals.get(path) {
            return Some(value.clone());
        }
        if let Some((head, tail)) = path.split_once('.')
            && let Some(value) = extra_locals.get(head)
            && let Some(value) = resolve_path(value, tail)
        {
            return Some(value.clone());
        }
    }
    let mut current = Some(entity);
    while let Some(candidate) = current {
        if let Ok(local_state) = states.get(candidate)
            && let Some(value) = local_state.0.get(path)
        {
            return Some(value.clone());
        }
        if let Some((head, tail)) = path.split_once('.')
            && let Ok(local_state) = states.get(candidate)
            && let Some(value) = local_state.0.get(head)
            && let Some(value) = resolve_path(value, tail)
        {
            return Some(value.clone());
        }
        if let Ok(root_computed) = computed.get(candidate)
            && let Some(value) = resolve_computed_path(
                candidate,
                path,
                parents,
                states,
                computed,
                roots,
                values,
                ref_rects,
                root_computed,
                stack,
            )
        {
            return Some(value);
        }
        if let Ok(root) = roots.get(candidate)
            && let Some(value) = resolve_path(&root.0, path)
        {
            return Some(value.clone());
        }
        current = parents.get(candidate).ok().map(ChildOf::parent);
    }
    values.get(path).cloned()
}

fn resolve_computed_path(
    entity: Entity,
    path: &str,
    parents: &Query<&ChildOf>,
    states: &Query<&DeclarativeLocalState>,
    computed: &Query<&DeclarativeRootComputedLocals>,
    roots: &Query<&DeclarativeRootViewModel>,
    values: &DeclarativeUiRuntimeValues,
    ref_rects: &DeclarativeRefRects,
    root_computed: &DeclarativeRootComputedLocals,
    stack: &mut Vec<String>,
) -> Option<UiValue> {
    let (head, tail) = path
        .split_once('.')
        .map_or((path, None), |(head, tail)| (head, Some(tail)));
    let expr = root_computed.0.get(head)?;
    if stack.iter().any(|name| name == head) {
        return None;
    }
    stack.push(head.to_string());
    let value = evaluate_runtime_expr(
        entity, expr, parents, states, computed, roots, values, ref_rects, stack,
    );
    stack.pop();
    let value = value?;
    match tail {
        Some(tail) => resolve_path(&value, tail).cloned(),
        None => Some(value),
    }
}