cadmpeg-ir 0.6.5

CAD data model, validation, diffing, and codec API for cadmpeg.
// SPDX-License-Identifier: Apache-2.0
#![allow(clippy::unwrap_used)]

use crate::features::{BodyMember, BodyMembers, BodySelection};
use crate::ids::{BodyId, HistoricalBodyId};

#[test]
fn ordered_members_reject_empty_sets_and_duplicates() {
    let a: BodyId = "test:selection:body#a".try_into().unwrap();
    let b: BodyId = "test:selection:body#b".try_into().unwrap();
    for (bodies, native) in [
        (Vec::new(), Vec::new()),
        (vec![a.clone(), a.clone()], vec!["a", "b"]),
        (vec![a, b], vec!["a", "a"]),
    ] {
        let json_rows = bodies
            .iter()
            .zip(&native)
            .map(|(body, native)| serde_json::json!({"body": body, "native": native}))
            .collect::<Vec<_>>();
        let rows = bodies
            .into_iter()
            .zip(native)
            .map(|(body, native)| {
                BodyMember::new(
                    body,
                    cadmpeg_core::text::NonBlankString::new(native.to_owned())
                        .expect("non-blank native fixture"),
                )
            })
            .collect::<Vec<_>>();
        assert!(BodyMembers::try_from_rows(rows).is_err());
        assert!(
            serde_json::from_value::<BodyMembers<BodyId>>(serde_json::json!(json_rows)).is_err()
        );
    }
}

#[test]
fn historical_body_members_refuse_the_deleted_parallel_arrays() {
    let a: HistoricalBodyId = "a".try_into().unwrap();
    let b: HistoricalBodyId = "b".try_into().unwrap();
    for (bodies, native) in [
        (Vec::new(), Vec::new()),
        (vec![a.clone(), a.clone()], vec!["a", "b"]),
        (vec![a.clone(), b.clone()], vec!["a", "a"]),
    ] {
        let rows = bodies
            .into_iter()
            .zip(native)
            .map(|(body, native)| {
                BodyMember::new(
                    body,
                    cadmpeg_core::text::NonBlankString::new(native.to_owned())
                        .expect("non-blank native fixture"),
                )
            })
            .collect::<Vec<_>>();
        assert!(BodyMembers::try_from_rows(rows).is_err());
    }
    assert!(cadmpeg_core::text::NonBlankString::new(" ").is_none());

    let members = BodyMembers::try_from_rows(vec![
        BodyMember::new(
            b.clone(),
            cadmpeg_core::text::NonBlankString::new("native-first").expect("non-blank fixture"),
        ),
        BodyMember::new(
            a.clone(),
            cadmpeg_core::text::NonBlankString::new("native-second").expect("non-blank fixture"),
        ),
    ])
    .unwrap();
    assert_eq!(members.bodies().collect::<Vec<_>>(), [&b, &a]);
    assert_eq!(
        members
            .0
            .iter()
            .map(|row| row.native.as_str())
            .collect::<Vec<_>>(),
        ["native-first", "native-second"]
    );
    let selection = BodySelection::HistoricalSet {
        state: "test:selection:feature_input_topology#state"
            .try_into()
            .unwrap(),
        members,
    };
    let wire = serde_json::to_value(&selection).unwrap();
    assert!(wire["value"].get("bodies").is_none());
    assert_eq!(
        serde_json::from_value::<BodySelection>(wire.clone()).unwrap(),
        selection
    );

    let mut restated = wire;
    let value = restated["value"].as_object_mut().unwrap();
    value.insert("bodies".into(), serde_json::json!(["b", "a"]));
    value.insert(
        "native".into(),
        serde_json::json!(["native-first", "native-second"]),
    );
    let error = serde_json::from_value::<BodySelection>(restated)
        .unwrap_err()
        .to_string();
    assert!(error.contains("unknown field `bodies`"), "{error}");
}