cadmpeg-ir 0.6.2

CAD data model, validation, diffing, and codec API for cadmpeg.
// SPDX-License-Identifier: Apache-2.0
//! Spreadsheet reference and layout validation.

use super::{record_finding, CadIr, Finding};
use crate::index::identities::BorrowedIdentities;
use crate::report::check::Check;

pub(super) fn check_spreadsheets(ir: &CadIr, findings: &mut Vec<Finding>) {
    let features = BorrowedIdentities::build(|add| {
        for feature in &ir.model.features {
            add(feature.id.as_str(), ());
        }
    });
    let parameters = BorrowedIdentities::build(|add| {
        for parameter in &ir.model.parameters {
            add(parameter.id.as_str(), parameter);
        }
    });
    for sheet in &ir.model.spreadsheets {
        if !features.contains(sheet.feature.as_str()) {
            record_finding(
                findings,
                Check::ReferentialIntegrity,
                crate::report::Severity::Error,
                Some(sheet.id.as_str()),
                format_args!("{}", "spreadsheet feature does not resolve"),
            );
        }
        for cell in sheet.cells() {
            let Some(parameter) = parameters.get(cell.parameter.as_str()) else {
                record_finding(
                    findings,
                    Check::ReferentialIntegrity,
                    crate::report::Severity::Error,
                    Some(sheet.id.as_str()),
                    format_args!("{}", "spreadsheet cell does not resolve"),
                );
                continue;
            };

            if parameter.owner.as_ref() != Some(&sheet.feature) {
                record_finding(
                    findings,
                    Check::ReferentialIntegrity,
                    crate::report::Severity::Error,
                    Some(sheet.id.as_str()),
                    format_args!("{}", "spreadsheet cell has a different owner"),
                );
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::check_spreadsheets;
    use crate::document::CadIr;

    fn parameter_fixture() -> CadIr {
        let mut ir = CadIr::empty();
        ir.model.parameters.push(
            serde_json::from_value(serde_json::json!({
                "id": "test:model:parameter#cell", "name": "cell", "expression": "7"
            }))
            .unwrap(),
        );
        ir
    }

    #[test]
    fn spreadsheet_reference_findings_keep_row_order_and_missing_owner_semantics() {
        let mut ir = parameter_fixture();
        ir.model.spreadsheets.push(
            serde_json::from_value(serde_json::json!({
                "id": "test:model:spreadsheet#sheet", "feature": "test:model:feature#missing",
                "cells": [
                    {"address": "A1", "parameter": "test:model:parameter#cell"},
                    {"address": "A2", "parameter": "test:model:parameter#missing"}
                ]
            }))
            .unwrap(),
        );
        let ctx = cadmpeg_test_support::service_decode_context();
        let mut findings = Vec::new();
        check_spreadsheets(&ir, &mut findings);
        assert_eq!(
            findings
                .iter()
                .map(|finding| finding.message.as_str())
                .collect::<Vec<_>>(),
            [
                "spreadsheet feature does not resolve",
                "spreadsheet cell has a different owner",
                "spreadsheet cell does not resolve"
            ]
        );
        for finding in findings {
            assert_eq!(
                finding.check,
                crate::report::check::Check::ReferentialIntegrity
            );
            assert_eq!(
                finding.entity.as_deref(),
                Some("test:model:spreadsheet#sheet")
            );
        }
        ctx.finish_session().unwrap();
    }
}