scena 1.7.2

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
use serde_json::json;

use super::checks::{checked_check, error_check, observed_pairs};
use crate::{
    SceneCompositionCheckV1, SceneInspectionReportV1, SceneRecipeBuildV1, SceneRecipeExpectV1,
};

pub(super) fn composition_state_checks(
    manifest: &SceneRecipeBuildV1,
    inspection: &SceneInspectionReportV1,
    expect: Option<&SceneRecipeExpectV1>,
) -> Vec<SceneCompositionCheckV1> {
    let mut checks = Vec::new();
    let Some(expect) = expect else {
        return checks;
    };
    for expected in &expect.expect_state {
        let id = format!("expect_state.{}", expected.id);
        let Some(import) = manifest
            .imports
            .iter()
            .find(|import| import.id == expected.import)
        else {
            checks.push(error_check(
                id,
                "state_variant",
                "state_import_missing",
                Some(expected.id.clone()),
                Vec::new(),
                observed_pairs([("import", json!(expected.import))]),
                (
                    "state expectation references an import missing from the build manifest",
                    "declare the import or update expect_state.import",
                ),
            ));
            continue;
        };
        let Some(inspection_import) = inspection.imports.as_ref().and_then(|imports| {
            imports
                .iter()
                .find(|candidate| candidate.handle == import.import_handle)
        }) else {
            checks.push(error_check(
                id,
                "state_variant",
                "state_import_not_inspected",
                Some(expected.id.clone()),
                vec![import.import_handle],
                observed_pairs([
                    ("import", json!(expected.import)),
                    ("import_handle", json!(import.import_handle)),
                ]),
                (
                    "state expectation import is absent from the inspection report",
                    "rerun inspection after building the recipe or remove the stale expectation",
                ),
            ));
            continue;
        };
        let expected_variant = expected.active_material_variant.as_deref();
        let actual_variant = inspection_import.active_variant.as_deref();
        let observed = observed_pairs([
            ("import", json!(expected.import)),
            ("expected_active_material_variant", json!(expected_variant)),
            ("actual_active_material_variant", json!(actual_variant)),
            (
                "available_material_variants",
                json!(inspection_import.material_variants),
            ),
        ]);
        if expected_variant == actual_variant {
            checks.push(checked_check(
                id,
                "state_variant",
                "material_variant_state_satisfied",
                Some(expected.id.clone()),
                vec![import.import_handle],
                observed,
                (
                    "active material variant state satisfies the declared expectation",
                    "no action needed",
                ),
            ));
        } else {
            checks.push(error_check(
                id,
                "state_variant",
                "material_variant_state_mismatch",
                Some(expected.id.clone()),
                vec![import.import_handle],
                observed,
                (
                    "active material variant state does not match the declared expectation",
                    "apply the intended material variant before rendering or update expect_state",
                ),
            ));
        }
    }
    checks
}