mavinspect 0.6.6

Library for parsing MAVLink XML definitions
Documentation
use mavinspect::parser::Inspector;
use std::collections::{HashMap, HashSet};

fn all_dialect_paths() -> Vec<&'static str> {
    vec![
        "./message_definitions/standard",
        "./message_definitions/extra",
    ]
}

fn inherit_dialect_paths() -> Vec<&'static str> {
    vec!["./message_definitions/extra/inheritance"]
}

#[test]
fn test_inheritance() {
    let inspector = Inspector::builder()
        .set_sources(&inherit_dialect_paths())
        .build()
        .unwrap();
    let protocol = inspector.parse().unwrap();

    let base = protocol.get_dialect_by_canonical_name("base").unwrap();
    let ext_a = protocol.get_dialect_by_canonical_name("ext_a").unwrap();
    let ext_b = protocol.get_dialect_by_canonical_name("ext_b").unwrap();
    let incl_asc = protocol.get_dialect_by_canonical_name("incl_asc").unwrap();
    let incl_dsc = protocol.get_dialect_by_canonical_name("incl_dsc").unwrap();
    let merge_ab = protocol.get_dialect_by_canonical_name("merge_ab").unwrap();

    // Strict inheritance
    {
        let enm_inherited_base = base.get_enum_by_name("INHERITED").unwrap();
        let enm_inherited_ext_a = ext_a.get_enum_by_name("INHERITED").unwrap();
        let enm_inherited_ext_b = ext_b.get_enum_by_name("INHERITED").unwrap();
        let enm_inherited_incl_asc = incl_asc.get_enum_by_name("INHERITED").unwrap();
        let enm_inherited_incl_dsc = incl_dsc.get_enum_by_name("INHERITED").unwrap();
        let enm_inherited_merge_ab = merge_ab.get_enum_by_name("INHERITED").unwrap();

        assert_eq!(
            enm_inherited_base.fingerprint(),
            enm_inherited_ext_a.fingerprint()
        );
        assert_eq!(
            enm_inherited_base.fingerprint(),
            enm_inherited_ext_b.fingerprint(),
        );
        assert_eq!(
            enm_inherited_base.fingerprint(),
            enm_inherited_incl_asc.fingerprint()
        );
        assert_eq!(
            enm_inherited_base.fingerprint(),
            enm_inherited_incl_dsc.fingerprint()
        );
        assert_eq!(
            enm_inherited_base.fingerprint(),
            enm_inherited_merge_ab.fingerprint()
        );
    }

    // Inheritance with redefinition
    {
        let enm_inherited_to_extend_base = base.get_enum_by_name("INHERITED_TO_EXTEND").unwrap();
        let enm_inherited_to_extend_ext_a = ext_a.get_enum_by_name("INHERITED_TO_EXTEND").unwrap();
        let enm_inherited_to_extend_ext_b = ext_b.get_enum_by_name("INHERITED_TO_EXTEND").unwrap();
        let enm_inherited_to_extend_incl_asc =
            incl_asc.get_enum_by_name("INHERITED_TO_EXTEND").unwrap();
        let enm_inherited_to_extend_incl_dsc =
            incl_dsc.get_enum_by_name("INHERITED_TO_EXTEND").unwrap();
        let enm_inherited_to_extend_merge_ab =
            merge_ab.get_enum_by_name("INHERITED_TO_EXTEND").unwrap();

        assert_ne!(
            enm_inherited_to_extend_base.fingerprint(),
            enm_inherited_to_extend_ext_a.fingerprint()
        );
        assert_ne!(
            enm_inherited_to_extend_base.fingerprint(),
            enm_inherited_to_extend_ext_b.fingerprint()
        );
        assert_eq!(
            enm_inherited_to_extend_merge_ab.fingerprint(),
            enm_inherited_to_extend_incl_asc.fingerprint()
        );
        assert_eq!(
            enm_inherited_to_extend_merge_ab.fingerprint(),
            enm_inherited_to_extend_incl_dsc.fingerprint()
        );
        assert_eq!(
            enm_inherited_to_extend_incl_asc.fingerprint(),
            enm_inherited_to_extend_incl_dsc.fingerprint()
        );
    }
}

#[test]
fn test_commands_inheritance() {
    let inspector = Inspector::builder()
        .set_sources(&all_dialect_paths())
        .build()
        .unwrap();
    let protocol = inspector.parse().unwrap();

    let all = protocol.get_dialect_by_canonical_name("all").unwrap();
    let ardupilotmega = protocol
        .get_dialect_by_canonical_name("ardupilotmega")
        .unwrap();
    let common = protocol.get_dialect_by_canonical_name("common").unwrap();
    let storm32 = protocol.get_dialect_by_canonical_name("storm32").unwrap();
    let asluav = protocol.get_dialect_by_canonical_name("asluav").unwrap();
    let matrixpilot = protocol
        .get_dialect_by_canonical_name("matrixpilot")
        .unwrap();

    {
        let mav_cmd_all = all.get_enum_by_name("MAV_CMD").unwrap();
        let mav_cmd_ardupilotmega = ardupilotmega.get_enum_by_name("MAV_CMD").unwrap();
        let mav_cmd_common = common.get_enum_by_name("MAV_CMD").unwrap();

        let mav_cmd_storm32 = storm32.get_enum_by_name("MAV_CMD").unwrap();
        let mav_cmd_asluav = asluav.get_enum_by_name("MAV_CMD").unwrap();
        let mav_cmd_matrixpilot = matrixpilot.get_enum_by_name("MAV_CMD").unwrap();

        assert_ne!(
            mav_cmd_all.fingerprint(),
            mav_cmd_ardupilotmega.fingerprint()
        );
        assert_ne!(
            mav_cmd_ardupilotmega.fingerprint(),
            mav_cmd_common.fingerprint()
        );

        assert_eq!(mav_cmd_all.defined_in(), "all");
        assert_eq!(mav_cmd_ardupilotmega.defined_in(), "ardupilotmega");
        assert_eq!(mav_cmd_common.defined_in(), "common");
        assert_eq!(mav_cmd_storm32.defined_in(), "storm32");
        assert_eq!(mav_cmd_asluav.defined_in(), "asluav");
        assert_eq!(mav_cmd_matrixpilot.defined_in(), "matrixpilot");

        let mut dialect_entry_values = HashMap::new();
        for mav_cmd in [
            mav_cmd_all,
            mav_cmd_ardupilotmega,
            mav_cmd_common,
            mav_cmd_storm32,
            mav_cmd_asluav,
            mav_cmd_matrixpilot,
        ] {
            let entry_values: HashSet<u32> =
                HashSet::from_iter(mav_cmd.entries().iter().map(|entry| entry.value()));
            dialect_entry_values.insert(mav_cmd.defined_in(), entry_values);
        }

        for dlct_name in ["all", "ardupilotmega", "storm32", "asluav", "matrixpilot"] {
            assert!(
                dialect_entry_values
                    .get(dlct_name)
                    .unwrap()
                    .is_superset(dialect_entry_values.get("common").unwrap()),
                "`MAV_CMD` entries in dialect `{}` should be a superset of `common`",
                dlct_name
            );
        }

        for dlct_name in ["all", "ardupilotmega", "storm32", "asluav"] {
            assert!(
                dialect_entry_values
                    .get(dlct_name)
                    .unwrap()
                    .is_subset(dialect_entry_values.get("all").unwrap()),
                "`MAV_CMD` entries in dialect `{}` should be a subset of `all`",
                dlct_name
            );
        }
    }

    {
        let cmd_int_all = all.get_message_by_name("COMMAND_INT").unwrap();
        let cmd_int_ardupilotmega = ardupilotmega.get_message_by_name("COMMAND_INT").unwrap();
        let cmd_int_common = common.get_message_by_name("COMMAND_INT").unwrap();

        let cmd_int_storm32 = storm32.get_message_by_name("COMMAND_INT").unwrap();
        let cmd_int_asluav = asluav.get_message_by_name("COMMAND_INT").unwrap();
        let cmd_int_matrixpilot = matrixpilot.get_message_by_name("COMMAND_INT").unwrap();

        assert_ne!(
            cmd_int_all.fingerprint(),
            cmd_int_ardupilotmega.fingerprint()
        );
        assert_ne!(
            cmd_int_ardupilotmega.fingerprint(),
            cmd_int_common.fingerprint()
        );

        assert!(cmd_int_all.was_defined_in("common"));
        assert!(cmd_int_all.was_defined_in("ardupilotmega"));

        assert!(cmd_int_storm32.was_defined_in("common"));
        assert!(cmd_int_storm32.was_defined_in("storm32"));

        assert!(cmd_int_asluav.was_defined_in("common"));
        assert!(cmd_int_asluav.was_defined_in("asluav"));

        assert!(cmd_int_matrixpilot.was_defined_in("common"));
        assert!(cmd_int_matrixpilot.was_defined_in("matrixpilot"));
    }
}